Swift小demo学习笔记

2017-07-26  本文已影响76人  黑化肥发灰

之前看了一些swift相关的介绍,但是一直没怎么写代码,但是不写代码肯定掌握不好一门语言的,纸上得来终觉浅,绝知此事要躬码。正好在github上找到了一个好东西,有三四十个小demo,我决定参考他们自己重写一遍了。本篇笔记记录下每个demo里面自己不熟的地方,后面可以随时复习。
参考链接如下:SwiftPractice

1.GHWSimpleClock

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        self.window = UIWindow(frame: CGRect(x: 0, y: 0, width: kScreenWidth, height: kScreenHeight))
        let vc = GHWViewController()
        self.window?.rootViewController = vc
        self.window?.makeKeyAndVisible()
        return true

    }
    [self.buttonLeft, self.buttonRight].forEach {
        ($0.addTarget(self, action: #selector(buttonTap(sender:)), for: .touchUpInside))
    }
    func buttonTap(sender : UIButton) {
        switch sender {
        case self.buttonLeft:
            sender.isSelected = !sender.isSelected
            sender.isSelected ? start() : stop()
            break
        case self.buttonRight:
            sender.isSelected = !sender.isSelected
            sender.isSelected ? pause() : continue1()
            break
        default:
            return
        }
    }

2. GHWCustomFont

return tableViewHeight/CGFloat(dataArray.count)
//设置状态栏样式
/*设置状态栏:
 @available(iOS 7.0, *)
 open var preferredStatusBarStyle: UIStatusBarStyle { get }//样式
 
 @available(iOS 7.0, *)
 open var prefersStatusBarHidden: Bool { get }//是否隐藏
*/
override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
}
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        /*代码创建,并且没有注册cell的情况下,用dequeueReusableCell(withIdentifier identifier: String) -> UITableViewCell?
         如果已经注册了,或者用的xib,就使用dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell
         */
        //??空合运算符,a ?? b,对可选类型a进行判断,为nil默认值为b,不为空就解封
        let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) ?? UITableViewCell(style: .subtitle, reuseIdentifier: reuseIdentifier)
        let text = datas[indexPath.row]
        
        cell.textLabel?.text = text
        cell.textLabel?.textColor = .white
        cell.textLabel?.font = UIFont(name: fontNames[fontNumber], size: 24)
        cell.backgroundColor = .black
        
        return cell
    }

3. PlayLocalVideo

struct VideoModel {
    let image: String
    let title: String
    let source: String
}
// 不加问号的话会报错:class has no initializers, 这里凡是没有初始化的都要加上问号
var player : AVPlayer?
var playerViewController : AVPlayerViewController?
    let dataArray = [
        VideoModel(image: "videoScreenshot01", title: "Introduce 3DS Mario", source: "Youtube - 06:32"),
        VideoModel(image: "videoScreenshot02", title: "Emoji Among Us", source: "Vimeo - 3:34"),
        VideoModel(image: "videoScreenshot03", title: "Seals Documentary", source: "Vine - 00:06"),
        VideoModel(image: "videoScreenshot04", title: "Adventure Time", source: "Youtube - 02:39"),
        VideoModel(image: "videoScreenshot05", title: "Facebook HQ", source: "Facebook - 10:20"),
        VideoModel(image: "videoScreenshot06", title: "Lijiang Lugu Lake", source: "Allen - 20:30")
    ]

4. WelcomeView

        //遍历数组,同时获得index
        for (index,value) in images.enumerated() {
            let imageView = UIImageView(frame: CGRect(x: YHWidth*CGFloat(index), y: 0, width: YHWidth, height: YHHeight))
            imageView.image = UIImage(named: value)
            //限制边界
            imageView.clipsToBounds = true
            imageView.contentMode = .scaleAspectFill
            scrollBG.addSubview(imageView)
        }

5. PictureBrowse

// 毛玻璃
    let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
    let myLayout = UICollectionViewFlowLayout()
    myLayout.scrollDirection = .horizontal
    myLayout.itemSize = CGSize(width: kCellWidth, height: kCellHeight)
    myLayout.minimumLineSpacing = 20
    myLayout.minimumInteritemSpacing = 20
    myLayout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 0)
    
    collectionView = UICollectionView(frame: CGRect(x : 0, y : (kScreenHeight - kCellHeight)/2, width : kScreenWidth, height : kCellHeight), collectionViewLayout: myLayout)
    collectionView.delegate = self;
    collectionView.dataSource = self;
    collectionView.backgroundColor = .clear
    collectionView.register(GHWCollectionViewCell.self, forCellWithReuseIdentifier: identifier)
    var data: CollectionModel? {
        /*属性观察器
         willSet 在新的值被设置之前调用
         didSet 在新的值被设置之后立即调用
         */
        didSet {
            updateUI()
        }
    }
    private func updateUI() {
        featureImageView.image = data?.featuredImage
        interestTitleLabel.text = data?.title
        interestDetailLabel.text = data?.descriptions
    }

static func createInterests() -> [CollectionModel] {
    return [
        CollectionModel(title: "Training like this, #bodyline", descriptions: "Create beautiful apps. We walked to Antartica yesterday, and camped with some cute pinguines, and talked about this wonderful app idea. 🐧⛺️✨", featuredImage: UIImage(named: "bodyline")!)
    ]
}
    var title: String?
    var featureImage: UIImage?
    var descriptions: String?
    
    // 这边不需要override,只有真正敲代码才能知道这些问题了,所以删掉
    init(title: String, descriptions: String, featureImage: UIImage) {
        self.featureImage = featureImage
        self.title = title
        self.descriptions = descriptions
    }

6. Current Location


下面这样是好的:


7. SystemRefreshControl

    let newData = ["1111", "2222", "3333", "4444"]
    var dataArray = ["11", "22", "33", "44"]
    let identifier = "identifier"
    func addData() {
        dataArray.append(contentsOf: newData)
        tableView.reloadData()
        refreshControl.endRefreshing()
    }

8 GradientColor

import UIKit
let YHRect = UIScreen.main.bounds
let YHHeight = YHRect.size.height
let YHWidth = YHRect.size.width
class ViewController: UIViewController {
    let gradientLayer = CAGradientLayer()
    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
    }
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    func setupView() {
        setupGradientLayer()
        Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(randomColor), userInfo: nil, repeats: true)
    }
    func setupGradientLayer() {
        gradientLayer.frame = YHRect
        let color1 = UIColor(white: 0.5, alpha: 0.2).cgColor
        let color2 = UIColor(red: 1.0, green: 0, blue: 0, alpha: 0.4).cgColor
        let color3 = UIColor(red: 0, green: 1, blue: 0, alpha: 0.3).cgColor
        let color4 = UIColor(red: 0, green: 0, blue: 1, alpha: 0.3).cgColor
        let color5 = UIColor(white: 0.4, alpha: 0.2).cgColor
        gradientLayer.colors = [color1, color2, color3, color4, color5]
        gradientLayer.locations = [0.10, 0.30, 0.50, 0.70, 0.90]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        view.layer.addSublayer(gradientLayer)
    }
    func randomColor() {
        view.backgroundColor = UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0)
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

9. GradientColor

import UIKit

class GHWMainViewController: UIViewController, UIScrollViewDelegate {

    let bottomView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
    let scrollView = UIScrollView(frame: kScreenRect)
    let imageView = UIImageView(image: UIImage(named: "steve"))
    override func viewDidLoad() {
        super.viewDidLoad()
        configUI()
    }
    
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

    func configUI()  {
        view.layer.contents = UIImage(named: "steve")?.cgImage
        bottomView.frame = kScreenRect
        scrollView.backgroundColor = .clear
        scrollView.delegate = self
        scrollView.contentSize = imageView.bounds.size
        scrollView.addSubview(imageView)
        view.addSubview(bottomView)
        view.addSubview(scrollView)
        
        setupZoomScale()
        scrollView.zoomScale = scrollView.minimumZoomScale
        reCenterImage()
    }
    
    func setupZoomScale() {
        let scrollViewSize = scrollView.bounds.size
        let imageSize = imageView.bounds.size
        let widthScale = scrollViewSize.width / imageSize.width
        let heightScale = scrollViewSize.height / imageSize.height
        let miniScale = min(widthScale, heightScale)
        scrollView.minimumZoomScale = miniScale
        scrollView.maximumZoomScale = 3.5
    }
    
    func reCenterImage() {
        let scrollViewSize = scrollView.bounds.size
        let imageSize = imageView.bounds.size
        let horizontalSpace = imageSize.width < scrollViewSize.width ? (scrollViewSize.width - imageSize.width)/2.0 : 0
        let verticalSpace = imageSize.height < scrollViewSize.height ? (scrollViewSize.height - imageSize.height)/2.0 : 0
        scrollView.contentInset = UIEdgeInsets(top: verticalSpace, left: horizontalSpace, bottom: verticalSpace, right: horizontalSpace)
    }
    
    
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        reCenterImage()
    }
    
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
}

10. VideoBackground

import Foundation
import UIKit
extension UIButton {
    func buttonTitle(configTitle title: String) {
        setTitle(title, for: .normal)
        titleLabel?.textColor = .white
        layer.cornerRadius = 5
        layer.borderColor = UIColor.white.cgColor
        layer.borderWidth = 1
    }
}
func configMediaPlayer() {
    let url = URL(fileURLWithPath: Bundle.main.path(forResource: "moments", ofType: "mp4")!)
    mediaPlayerVC.player = AVPlayer(url: url)
    mediaPlayerVC.videoGravity = AVLayerVideoGravityResizeAspectFill
    mediaPlayerVC.showsPlaybackControls = false
    mediaPlayerVC.view.alpha = 1
    mediaPlayerVC.view.frame = kScreenRect
    NotificationCenter.default.addObserver(self, selector: #selector(repeatPlay), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: mediaPlayerVC.player?.currentItem)
    view.addSubview(mediaPlayerVC.view)
    view.sendSubview(toBack: mediaPlayerVC.view)
    mediaPlayerVC.player?.play()
}

func repeatPlay() {
    mediaPlayerVC.player?.seek(to: kCMTimeZero)
    mediaPlayerVC.player?.play()
}

11. colorProgress

import UIKit

class GHWMainViewController: UIViewController {
    let progresss = GHWProgress(frame:CGRect(x: 20, y: 200, width: kScreenWidth - 40, height: 20))
    override func viewDidLoad() {
        super.viewDidLoad()
        configUI()
    }
    func configUI() {
        view.addSubview(progresss)
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
            self.progresss.progress = self.progresss.progress + 0.03
            if self.progresss.progress >= 1.0 {
                timer.invalidate()
            }
        }
    }
}
import UIKit

class GHWProgress: UIView, CAAnimationDelegate {

    let gradientLayer = CAGradientLayer()
    let maskLayer = CALayer()
    
    var progress: CGFloat = 0.0 {
        didSet {
            reConfigUI()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)

        configUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    func reConfigUI() {
        maskLayer.frame = CGRect(x: 0.0, y: 0.0, width: progress * bounds.size.width, height: bounds.size.height)
    }
    
    private func configUI() {
        let whiteBG = CALayer()
        whiteBG.frame = bounds
        whiteBG.cornerRadius = bounds.size.height / 2.0
        whiteBG.borderColor = UIColor.white.cgColor
        whiteBG.borderWidth = 1.0
        layer.addSublayer(whiteBG)
        
        gradientLayer.frame = bounds
        gradientLayer.cornerRadius = bounds.size.height / 2.0
        gradientLayer.borderColor = UIColor.white.cgColor
        gradientLayer.borderWidth = 1.0
        
        var colors = [CGColor]()
        for hue in stride(from: 0, through: 360, by: 5) {
            let color = UIColor(hue: CGFloat(hue)/360.0, saturation: 1.0, brightness: 1.0, alpha: 1.0).cgColor
            colors.append(color)
        }
        gradientLayer.colors = colors
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
        layer.addSublayer(gradientLayer)
        
        reConfigUI()
        maskLayer.cornerRadius = bounds.size.height/2.0
        maskLayer.backgroundColor = UIColor.white.cgColor
        gradientLayer.mask = maskLayer
        
    }
    
    private func performAnimation() {
        var colors = gradientLayer.colors
        let color = colors?.popLast() as! CGColor
        colors?.insert(color, at: 0)
        
        let animation = CABasicAnimation(keyPath: "colors")
        animation.fromValue = gradientLayer.colors
        animation.toValue = colors
        animation.duration = 0.2
        animation.fillMode = kCAFillModeForwards
        animation.delegate = self
        gradientLayer.add(animation, forKey: "gradient")
        gradientLayer.colors = colors
    }
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        performAnimation()
    }
}

12. TableHeadView

import UIKit

let YHRect = UIScreen.main.bounds
let YHHeight = YHRect.size.height
let YHWidth = YHRect.size.width

let HeadViewHeight = YHHeight/3.0

class ViewController: UIViewController {

    
    let datas = ["下","拉","可","以","出","现","很","神","奇","的","事","情","yo","yo","yo","yo","yo","yo"]
    let tableView = UITableView(frame: YHRect, style: .plain)
    let resueIdentifer = "CustomCell"
    let headView = UIImageView(frame: CGRect(x: 0.0, y: 0.0, width: YHWidth, height: HeadViewHeight))
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    
    func setupView() {
        self.automaticallyAdjustsScrollViewInsets = false
        
        headView.backgroundColor = .white
        headView.contentMode = .scaleAspectFill
        headView.clipsToBounds = true
        view.addSubview(headView)
        
        //加载图片,注意这里是URL,不是NSURL
        let url = URL(string: "http://c.hiphotos.baidu.com/zhidao/pic/item/5ab5c9ea15ce36d3c704f35538f33a87e950b156.jpg")
        let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
            guard let _ = data,error == nil else { return }
            //回到主线程
            DispatchQueue.main.sync {
                self.headView.image = UIImage(data: data!)
            }
        }
        task.resume()
        
        tableView.delegate = self
        tableView.dataSource = self
        tableView.tableFooterView = UIView()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: resueIdentifer)
        tableView.showsVerticalScrollIndicator = false
        //下面两句必不可少,否则会出现第一次加载时位置不对的情况,这个地方一定要注意哦,非常关键
        tableView.contentInset.top = HeadViewHeight
        tableView.contentOffset = CGPoint(x: 0.0, y: -HeadViewHeight)
//        tableView.scrollIndicatorInsets.top = HeadViewHeight//右边指示器的位置
        view.addSubview(tableView)
        view.sendSubview(toBack: tableView)
    }

}

extension ViewController:UITableViewDataSource, UITableViewDelegate {
    //MARK: - DataSource
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return datas.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: resueIdentifer, for: indexPath)
        cell.textLabel?.text = datas[indexPath.row]
        cell.textLabel?.textAlignment = .center
        return cell
    }
    
    //MARK: - Delegate
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let offsety = scrollView.contentOffset.y + scrollView.contentInset.top
        if offsety <= 0 {
            headView.frame = CGRect(x: 0.0, y: 0.0, width: YHWidth, height: HeadViewHeight-offsety)
        }else {
            let height = (HeadViewHeight-offsety) <= 0.0 ? 0.0 : (HeadViewHeight-offsety)
            headView.frame = CGRect(x: 0.0, y: 0.0, width: YHWidth, height: height)
            headView.alpha = height/HeadViewHeight
        }
    }
}

上面有涉及到tableView的,contentOffset,contentInset的使用,这里又有新的体会了。其实content包括各种header和footer,再加上cell。contentOffset就是content到frame边的距离。把握好这个就可以理解为什么开始的时候要
tableView.contentInset.top = HeadViewHeight
tableView.contentOffset = CGPoint(x: 0.0, y: -HeadViewHeight)
然后tableView.addSubView的时候,是add到content上面哦。
其实主要还是要知道哪些是content。

13. ChildVCTransition

import UIKit

let YHRect = UIScreen.main.bounds
let YHHeight = YHRect.size.height
let YHWidth = YHRect.size.width

let JumpNotification = "JUMP"

class RootVC: UIViewController {
    
    var currentChildNumber = 0
    
    //最简单的转场实现方式,该方式是在rootVC的子VC之间转场

    override func viewDidLoad() {
        super.viewDidLoad()
        setupChild()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    private func setupChild() {
        addChildViewController(ChildAVC())
        addChildViewController(ChildBVC())
        
        view.addSubview((childViewControllers.first?.view)!)
        
        //监听跳转通知,NotificationCenter通知中心
        NotificationCenter.default.addObserver(self, selector: #selector(jump), name: NSNotification.Name(rawValue: JumpNotification), object: nil)
    }
    
    func jump() {
        //options:跳转的方式
        transition(from: currentChildNumber==0 ? childViewControllers.first! : childViewControllers.last!,
                   to: currentChildNumber==0 ? childViewControllers.last! : childViewControllers.first!,
                   duration: 1,
                   options: currentChildNumber==0 ? .transitionFlipFromLeft : .transitionFlipFromRight,
                   animations: nil,
                   completion: nil)
        currentChildNumber = currentChildNumber == 0 ? 1 : 0
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: JumpNotification), object: nil)
    }
}
import UIKit

class ChildAVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .green
        //添加一个点击手势
        let tap = UITapGestureRecognizer(target: self, action: #selector(jump))
        view.addGestureRecognizer(tap)
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    func setupView() {
    }
    func jump() {
        //发送跳转通知
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: JumpNotification), object: nil)
    }
}

14. MusicPlayer

import UIKit
import AVFoundation

let YHRect = UIScreen.main.bounds
let YHHeight = YHRect.size.height
let YHWidth = YHRect.size.width

class ViewController: UIViewController {

    let fileManager = FileManager.default
    let musicPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first?.appending("/music")
    var musicList = [String]()
    let tableView = UITableView(frame: YHRect)
    let resueIndetifier = "MusicCell"
    var musicPlayer: AVAudioPlayer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        saveMusicFile()
        getMusicList()
        setupView()
    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    /*沙盒文件目录:
     AppName.app:应用程序本身数据,只读。  Bundle.main.path(forResource: <#T##String?#>, ofType: <#T##String?#>)
     Documents:会被itunes备份,应用程序产生的数据,不可再生的数据放在这里
        Inbox:该目录可被外部应用程序访问,只读,需要自己创建
     Library:
        Caches:应用程序启动过程需要的信息,不会被备份,存放可再生的数据
        Preferences:偏好设置,会被itunes备份
     tmp:临时数据存放,会自动被删除
     */
    
    //保存音乐文件--这一步不是必须的,只是为了熟悉swift存储文件的操作
    func saveMusicFile() {
        let names = ["成都","童话镇"]
        //在caches创建一个music文件夹
        /*try try! try?区别:
         try:两种情况,①do {let result = try func()} catch {} 处理throw ②let result = try func() 忽略throw
         try!:相当于可选类型!,如果不throw就不会出任何问题,一旦throw,程序就会crash
         try?:把throw转换成是否是nil,而不处理具体的结果。如①if let result = try? func() {} else {} ②guard let result = try? func() else {}
         */
        print(musicPath)
        do {
            try fileManager.createDirectory(atPath: musicPath!, withIntermediateDirectories: true, attributes: nil)
            print("创建成功")
        } catch let err as NSError {
            print("创建失败:\(err.localizedFailureReason)")
            return
        }
        //把歌曲保存到caches里面
        for name in names {
            let bundlePath = Bundle.main.path(forResource: name, ofType: "m4r")
            let toPath = musicPath!.appending("/\(name).m4r")
            if fileManager.fileExists(atPath: toPath) {
                print("已经存在\(name)")
                continue
            }
            do {
                try fileManager.copyItem(atPath: bundlePath!, toPath: toPath)
                print("移动成功")
            } catch let err as NSError {
                print("移动失败:\(err.localizedFailureReason)")
            }
        }
    }
    
    //读取音乐列表
    func getMusicList() {
        //读取、查看文件用NSFileManager
        do {
            musicList = try fileManager.contentsOfDirectory(atPath: musicPath!)
            print("查询成功:\(musicList)")
        } catch let err as NSError {
            print("查询失败:\(err.localizedFailureReason)")
        }
    }
    
    func setupView() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.tableFooterView = UIView()
        view.addSubview(tableView)
    }
    
    func playMusic(path: String) {
        if musicPlayer != nil {
            musicPlayer?.stop()
            musicPlayer = nil
        }
        do {
            try musicPlayer = AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
            //可以通过musicPlayer的属性获得歌曲的信息,包括长度,通道等信息
            print("歌曲长度:\((musicPlayer?.duration)!)")
            musicPlayer?.numberOfLoops = -1//循环次数
            musicPlayer?.delegate = self
            musicPlayer?.play()
            
        } catch let err as NSError {
            print("播放失败:\(err.localizedFailureReason)")
        }
        
    }

}

extension ViewController:UITableViewDataSource, UITableViewDelegate ,AVAudioPlayerDelegate {
    //MARK: - DataSource
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return musicList.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: resueIndetifier) ?? UITableViewCell(style: .default, reuseIdentifier: resueIndetifier)
        cell.textLabel?.text = musicList[indexPath.row]
        cell.textLabel?.textAlignment = .center
        cell.textLabel?.textColor = .orange
        cell.textLabel?.font = UIFont.systemFont(ofSize: 30, weight: 10)
        return cell
    }
    
    //MARK: - Delegate
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        playMusic(path: musicPath!.appending("/\(musicList[indexPath.row])"))
    }
    
    //MARK: - AVAudioPlayerDelegate
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        musicPlayer = nil
    }
}


15. ViewController转场动画

首先一个小问题,button上面图片下面标题,extension,这个很多地方都会有需要

import Foundation
import UIKit

extension UIButton {
    //设置按钮图片在上,文字在下的效果
    func alignContentVerticallyByCenter() {
        contentHorizontalAlignment = .center
        contentVerticalAlignment = .center
        
        //图片与title之间有一个默认的间隔10
        let offset: CGFloat = 10
        
        //在有的iOS版本上,会出现获得不到frame的情况,加上下面这两句可以100%得到
//        titleLabel?.backgroundColor = backgroundColor
//        imageView?.backgroundColor = backgroundColor
        
        //title
        let titleWidth = titleLabel?.frame.size.width
        let titleHeight = titleLabel?.frame.size.height
        let titleLef = titleLabel?.frame.origin.x
        let titleRig = frame.size.width-titleLef!-titleWidth!
        
        //image
        let imageWidth = imageView?.frame.size.width
        let imageHeight = imageView?.frame.size.height
        let imageLef = imageView?.frame.origin.x
        let imageRig = frame.size.width-imageLef!-imageWidth!
        
        imageEdgeInsets = UIEdgeInsets(top: 0, left: -imageLef!, bottom: titleHeight!, right: -imageRig)
        titleEdgeInsets = UIEdgeInsets(top: imageHeight!+offset, left: -titleLef!, bottom: 0, right: -titleRig)
    }
}

然后就是UIViewControllerAnimatedTransitioning和UIViewControllerTransitioningDelegate

UIViewControllerAnimatedTransitioning:这个接口负责切换的具体内容,也即“切换中应该发生什么”。开发者在做自定义切换效果时大部分代码会是用来实现这个接口。它只有两个方法需要我们实现。
UIViewControllerTransitioningDelegate:这个接口的作用比较简单单一,在需要VC切换的时候系统会像实现了这个接口的对象询问是否需要使用自定义的切换效果。这个接口共有四个类似的方法。

具体实现代码看下面:

import UIKit

class MenuTransitionManger: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

    
    private var presenting = false

    
    func offStage(amount: CGFloat) -> CGAffineTransform {
        //返回平移x,y的结构体
        return CGAffineTransform(translationX: amount, y: 0)
    }
    
    //关

    func offStageMenuController(menuVC: MenuViewController) {
        menuVC.view.alpha = 0
        
        let topRowOffset: CGFloat = 300
        let middleRowOffset: CGFloat = 150
        let bottomRowOffset: CGFloat = 50
        
        menuVC.btns[0].transform = offStage(amount: -topRowOffset)
        menuVC.btns[1].transform = offStage(amount: topRowOffset)
        menuVC.btns[2].transform = offStage(amount: -middleRowOffset)
        menuVC.btns[3].transform = offStage(amount: middleRowOffset)
        menuVC.btns[4].transform = offStage(amount: -bottomRowOffset)
        menuVC.btns[5].transform = offStage(amount: bottomRowOffset)
    }
    
    //开
    func onStageMenuController(menuVC: MenuViewController) {
        menuVC.view.alpha = 1
        
        menuVC.btns[0].transform = CGAffineTransform.identity
        menuVC.btns[1].transform = CGAffineTransform.identity
        menuVC.btns[2].transform = CGAffineTransform.identity
        menuVC.btns[3].transform = CGAffineTransform.identity
        menuVC.btns[4].transform = CGAffineTransform.identity
        menuVC.btns[5].transform = CGAffineTransform.identity
    }
    
    //MARK: - UIViewControllerAnimatedTransitioning
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let screens: (from: UIViewController, to: UIViewController) = (transitionContext.viewController(forKey: .from)!, transitionContext.viewController(forKey: .to)!)
        let menuVC = !presenting ? screens.from as! MenuViewController : screens.to as! MenuViewController
        let bottomVC = !presenting ? screens.to : screens.from
        let menuV = menuVC.view
        let bottomV = bottomVC.view
        
        if self.presenting {
            offStageMenuController(menuVC: menuVC)
        }
        
        container.addSubview(bottomV!)
        container.addSubview(menuV!)
        
        let duration = self.transitionDuration(using: transitionContext)
        
        UIView.animate(withDuration: duration,
                       delay: 0.0,
                       usingSpringWithDamping: 0.7,
                       initialSpringVelocity: 0.8,
                       options: [],
                       animations: { 
                        self.presenting ? self.onStageMenuController(menuVC: menuVC) : self.offStageMenuController(menuVC: menuVC)
            },
                       completion: { finished in
                        transitionContext.completeTransition(true)
                        if self.presenting {
                            UIApplication.shared.keyWindow?.addSubview(screens.from.view)
                        }
                        UIApplication.shared.keyWindow?.addSubview(screens.to.view)
            }
        )
    }
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    
    //MARK: - UIViewControllerTransitioningDelegate
    
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        self.presenting = true
        return self
    }
    
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        self.presenting = false
        return self
    }

}

16. 多级列表


这个demo里面重点关注

  1. 数据结构struct City Province
  2. initDatas()中对数据的处理
  3. viewForHeaderInSection中对手势的添加
  4. 采用runtime为手势增加一个tag属性,标记手势。

import UIKit


struct City {
    
    let name: String
    let alias: String
}


struct Province {
    
    let name: String
    var citys: [City]
    var isOpen: Bool
}


class ViewController: UIViewController {
    
    
    let tableView = UITableView(frame: YHRect, style: .plain)
    let reuseIdentifer = "Cell"
    let reuseHeaderIdentifer = "HeaderCell"
    var datas = [Province]()
    

    override func viewDidLoad() {
        super.viewDidLoad()
        initDatas()
        setupView()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    
    func initDatas() {
        
        let dic = NSDictionary(contentsOfFile: Bundle.main.path(forResource: "Province", ofType: "plist")!)
        dic?.enumerateKeysAndObjects({ (key, value, roolback) in
            let values = value as! NSDictionary
            var citys = [City]()
            for k in values.allKeys {
                let city = City(name: k as! String, alias: values[k] as! String)
                citys.append(city)
            }
            let pro = Province(name: key as! String, citys: citys, isOpen: false)
            datas.append(pro)
        })
    }
    
    
    func setupView() {
        
        tableView.delegate = self
        tableView.dataSource = self
        tableView.tableFooterView = UIView()
        tableView.register(CustomHeaderView.self, forHeaderFooterViewReuseIdentifier: reuseHeaderIdentifer)
        view.addSubview(tableView)
    }
}


extension ViewController:UITableViewDataSource, UITableViewDelegate {
    
    //MARK: - DataSource
    func numberOfSections(in tableView: UITableView) -> Int {
        return datas.count
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let province = datas[section]
        return province.isOpen ? province.citys.count : 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifer) ?? UITableViewCell(style: .value1, reuseIdentifier: reuseIdentifer)
        let city = datas[indexPath.section].citys[indexPath.row]
        cell.textLabel?.text = city.name
        cell.textLabel?.textColor = .orange
        cell.textLabel?.textAlignment = .center
        cell.detailTextLabel?.text = city.alias
        cell.detailTextLabel?.font = UIFont.systemFont(ofSize: 12)
        return cell
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: reuseHeaderIdentifer) as! CustomHeaderView
        
        func addTap() {
            let tap = UITapGestureRecognizer(target: self, action: #selector(headerTap(sender:)))
            tap.tag = section
            view.addGestureRecognizer(tap)
        }
        
        print("手势数目:\(view.gestureRecognizers?.count)")
        if let grs = view.gestureRecognizers {
            grs.count > 0 ? (grs.first as! UITapGestureRecognizer).tag = section : addTap()
        }else {
            addTap()
        }
        view.buildUI(province: datas[section])
        return view
    }
    
    //MARK: - GR
    func headerTap(sender: UITapGestureRecognizer) {
        
        datas[sender.tag].isOpen = !datas[sender.tag].isOpen
        tableView.reloadSections([sender.tag], with: .fade)
    }
    
    //MARK: - Delegate
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 70
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }
    
}
var AssociatedObjectHandle: UInt8 = 0

//采用runtime为手势增加一个tag属性,标记手势。
extension UITapGestureRecognizer {
    
//    struct AddProperty {
//        static var tag: Int = 0
//    }
//    
    var tag: Int {
        get {
            return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! Int
        }
        set {
            objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
        }
    }
}

上一篇下一篇

猜你喜欢

热点阅读