Swift3.0 版时光电影项目笔记

Swift 刚学几天,以练手的心态改写以前一个 OC 的小项目,这里记录一些遇到的坑点。由于是初学者,各位大牛如果发现错误,欢迎指出。闲话不多说,下面就正式开始

1.私有属性和方法

在 Swift 中,是没有私有属性的,也就是说,只要在同一个命名空间(后面会有介绍),我们都可以访问的到。但是Swift中是有有 private 关键字的,根据特点决定,如果编写 App 的话,直接用默认的就好了,就是啥也不用敲,如果编写 Framework,请认真思考流程,认真设计,外部接口要设置 public,而一些不想让别人看见的就可以用 private 或者 internal 修饰了

2.Swift中的宏定义

在Swift中是没有宏定义的,但是我们可以使用公用函数来替代宏,比如

func kTabBarWidth(object: UITabBarController) -> CGFloat {return object.tabBar.frame.size.width}
func kTabBarHeight(object: UITabBarController) -> CGFloat {return object.tabBar.frame.size.height}
func kButtonWidth(object: UITabBarController) -> CGFloat {return kTabBarWidth(object: object)/CGFloat((object.viewControllers?.count)!)}

在公用函数中取不到的变量我们可以以参数的形式传入

3.Swift 中不同类型变量的运算

Swift 是强语言,如果两个不同类型的相加,必须进行类型转换,比如

let num1 = 10
let num2 = 9.9
let iSum = num1 + Int(num2)
let dSum = Double(num1) + num2

4.按钮添加点击事件

为按钮添加点击事件,这里使用的是#Selector(方法名)的样式

//按钮添加点击事件
button.addTarget(self, action: #selector(selectedVC(button:)), for: UIControlEvents.touchUpInside)

值得注意的是,我们后面的按钮点击方式的枚举类型应该用
枚举名+“.”+枚举值
的方式来调用

5.闭包中的self与循环引用

Swift 中的闭包,也就是 OC 中的 Block ,怎么使用这里就不多叙述了,我们这里要说的,是要特别注意的点
在闭包中使用本类的属性或调用方法,必须使用Self调用,这样的话,就产生了循环引用的问题,和OC中相差不多,我们利用一个 weak 关键字来解决这一问题

let homeViewModel = HomeViewModel()
weak var weakSelf = self
homeViewModel.loadMovieData { (data) in
    weakSelf!.dataList = data
}

6.Swift中的命名空间

Objective-C 是没有命名空间的,在应用开发时,所有的代码和引用的静态库最终都会被编译到同一个域和二进制中。这样的后果是一旦我们有重复的类名的话,就会导致编译时的冲突和失败。在 Swift 中,由于可以使用命名空间了,即使是名字相同的类型,只要是来自不同的命名空间的话,都是可以和平共处的。Swift 中的命名空间的使用不是一个项目,而是需要跨项目,在一个项目中,都是一个命名空间,在同一个命名空间下,所有全局变量或者函数共享,不需要 import
我们项目中使用到了命名空间,在我们动态创建控制器的时候

let imgNames = ["home","payticket","store","discover","myinfo"]
//创建标签控制器数组存储标签控制器名
let viewContorllersArray = ["HomeViewController","PayTicketViewController","StoreViewController","DiscoverViewController","MyInfoViewController"]
        
var bnvVcArray:[UIViewController] = []
        
for i in 0..<5 {
    let str = viewContorllersArray[i]
    //通过一个字符串创建控制器对象
    //获取命名空间
    //namespace在info.plist 对应的是 CFBundleExecutable,我们可以在info.plist中任意右击一行,选中Show Raw Keys/Values
    let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
    let uivcType = NSClassFromString(namespace + "." + str) as? UIViewController.Type
    //可选绑定
    if let type = uivcType {
        //创建
        let uiVC = type.init()
        uiVC.tabBarItem.selectedImage = UIImage(named: imgNames[i] + "_on")
        uiVC.tabBarItem.image = UIImage(named: imgNames[i])
        uiVC.title = imgNames[i]
        let bnv = BaseNavViewController(rootViewController: uiVC)
        bnvVcArray.append(bnv)
    }
}

如果新建项目时,项目名称中包含有中文,可以进入是 Build Settings 中选中 "All" ,搜索 product name ,即可修改命名空间,如图:


2373BC66-3171-4EE9-998F-D715DD43D680.png

7.使用 Runtime 实现字典转模型

我们在项目中自定义了一个 BaseModel 自定义了一个构造方法传入一个字典,来方便的实现字典转模型,在实现过程中,我们在 Swift 中使用了 Runtime
我们知道 OC 是动态语言,能够通过 runtime API 调用和替换任意方法,纯 Swift 类的函数调用已经不再是 OC 的运行时发消息,而是在编译时就确定了调用哪个函数,所以没法通过 runtime 获取方法、属性,而Swift为了兼容 OC ,凡是继承自 NSObject 的类都会保留其动态性,所以我们能通过 runtime 拿到继承与 NSObject 的类的方法和属性

func setAttribut(dic: [String:Any]) -> Void {
    let attributDic = attributesDic(dic: dic)
    
    //Runtime获取本类属性
    var count:UInt32 = 0
    let ivars = class_copyIvarList(self.classForCoder, &count)
    for i in 0..<count {
        //取出属性名
        let ivar = ivars?[Int(i)]
        let ivarName = ivar_getName(ivar!)
        let nName = String(cString: ivarName!)
        //取出要赋值的值
        let attribut = attributDic[nName]
        var value:NSObject
        if dic[attribut!] != nil {
            value = dic[attribut!] as! NSObject
        } else {
            value = "" as NSObject
        }   
 
        //利用KVC给本类的属性赋值
        self.setValue(value, forKey: nName)
           
    }
        
}

8.Swift 中的异常处理

在 OC 中调用方法时,通常是通过一个 NSError 参数来返回异常信息的,但是在Swift中返回异常的方式就有些不同了。下面,我们自定义一个返回异常的函数,并且调用这个函数

//定义一个抛出异常的方法
//在一切正常的情况下,返回值是String类型,
func someFunctionWhichCanFail(param: Int) throws -> String {       
    if param > 0 {
        return "成功"
    } else {
        throw NSError(domain: "啦啦啦,失败了", code: 499, userInfo: nil)
    }   
}
//do-try-catch这种错误模式,本意就是尝试(try)做一件事情,如果失败则捕获(catch)处理。
//要注意的是你可以在 do 代码段中写多于一行的代码(并且 try 可以调用不止一个抛错误的方法)。如果一切顺利的话,将会像预期的那样执行那些方法,但是一旦方法出错就会跳出 do 代码段,进入 catch 处。
do {
    //尝试做一件事情
    let result = try someFunctionWhichCanFail(param: -1)
    print("\(result)")
} catch let error {
    //在catch中捕获错误信息
    print("\(error)")
}

这样,我们就基本明白了 Swift 中处理异常的基本方式,再看我们的项目。
在项目中,我们封装了一个JSON文件解析类,这个类有一个方法,传入JSON文件名,返回一个解析好的字典或是数组

class func jsonObjectFromFileName(fileName: String) -> NSDictionary? {
    //获取文件路径
    let path = Bundle.main.path(forResource: fileName, ofType: "json")
    //解析
    let data = NSData.init(contentsOfFile: path!)
    let dic = try! JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.allowFragments) as? NSDictionary
        
    return dic
}

这里的
JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.allowFragments)
方法是要求我们必须捕获一个异常的,我们这里代码中使用了一个 try! 关键字,这里还可以使用 try? 关键字。try? 会将错误转换为可选值,当调用 try? +函数或方法语句 的时候,如果函数或方法抛出错误,程序不会发崩溃,而返回一个nil,如果没有抛出错误则返回可选值,不会含有更多的造成特定的错误或者异常原因的信息。try! 打破了错误传播链条,但是如果真的发生错误就出现运行期错误,导致程序的崩溃。所以使用 try! 打破错误传播链条时,应该确保程序不会发生错误

9.Swift 项目中使用 CocoaPods

Swift 项目中使用 Cocoapods 导入第三方库,导入的过程与 OC 项目无异,但是使用时,需要创建一个 Bridging-Header.h 桥接头文件,来实现 OC 与 Swift 混编


screenshot.png

可以在Building Settings中自己设置桥接头文件

screenshot 2.png

这里有个简便方法生成这个桥接文件,就是我们在项目中创建一个 OC 文件,Xcode就会自动帮我们生成一个桥接头文件
有了头文件之后,我们只需要在桥接文件中导入我们需要使用的第三方库,就可以愉快的使用了~~~~

screenshot 3.png

10.子类实现父类的代理方法

在这里,让我们首先看项目中的例子
我们声明了一个 BaseCollectionView 继承于 UICollectionView ,并将代理设置为自己


screenshot.png

然后我们又声明了一个 PosterCollectionView 继承了BaseCollectionView


screenshot.png

在 OC 中,我们想要在 PosterCollectionView 里实现 UICollectionView 的 delegate 方法,那么直接在子类 PosterCollectionView 中书写就可以了,但是在Swift中,我们需要在父类 BaseCollectionView 中实现,然后在子类 PosterCollectionView 中重写

父类:


screenshot.png

子类:

screenshot.png

11.Swift 中的 KVO

Swift 中的 KVO 使用与 OC 相差不多,但是还是有一些坑点,下面我们只是简单的说一下
1.观察者和被观察者都必须是 NSObject 的子类,因为 OC 中 KVO 的实现基于 KVC 和 runtime 机制,只有是 NSObject 的子类才能利用这些特性
2.要观察的属性使用 @dynamic 修饰,表示该属性的存取都由 runtime 在运行时来决定,由于 Swift 基于效率的考量默认禁止了动态派发机制,因此要加上该修饰符来开启动态派发
这里是项目中的实现代码

//被观察的属性,加dynamic关键字
//记录下标
dynamic var currentIndex:Int = 0
    //添加监听
    func addObserver() -> Void {
        
        //添加indexCollectionView的监听
        indexCollectionView?.addObserver(self, forKeyPath: "currentIndex", options: NSKeyValueObservingOptions.new, context: nil)
        
        //添加posterCollectionView的监听
        posterCollectionView?.addObserver(self, forKeyPath: "currentIndex", options: NSKeyValueObservingOptions.new, context: nil)
        
    }
    
    //观察者方法
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        //变化后的值
        let index = change?[NSKeyValueChangeKey.newKey] as! Int
        
        //创建indexPath
        let indexPath = IndexPath(item: index, section: 0)
        
        //判断是否是currenIndex属性
        guard keyPath == "currentIndex" else {
            return
        }
        
        //判断对象的类型
        if object is PosterCollectionView {
            
            //滑动到指定单元格
            indexCollectionView?.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredHorizontally, animated: true)
            
        } else if object is IndexCollectionView{
            
            //滑动到指定单元格
            posterCollectionView?.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredHorizontally, animated: true)
            
        }
        
        //变换标题
        titleLabel?.text = dataList?[index].titleCn
        
    }
    
    //销毁
    deinit {
        //移除观察者
        indexCollectionView?.removeObserver(self, forKeyPath: "currentIndex")
        posterCollectionView?.removeObserver(self, forKeyPath: "currentIndex")
    }

这里注意一定要在析构函数中移除监听
如果你还对 Swift 中的 KVO 感兴趣的话,可以看一下这篇文章
Swift: KVO 注意事项和属性观察器

在最后,放上小项目的地址,还是那句话,说再多也不如自己敲一敲
时光电影Swift版初学小项目

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,065评论 4 62
  • 忘记 怎么可能 如果 有来世 我宁愿 不喝孟婆汤 记着 你的容颜 追寻 最后一个她
    彼岸橘阅读 263评论 0 1
  • 前言 FMDB是以OC的方式封装了SQLite的C语言API,使用起来更加面向对象,省去了很多麻烦、冗余的C语言代...
    若小北00阅读 832评论 0 3
  • 请认真思考一下,你的公司是不是存在着这样的普遍现象—— 有5%~10%的公司员工,一上班就是来挑毛病、和你对着干,...
    小声讲故事阅读 268评论 0 0
  • 从昨天晚上开始就特别的期待今晚的“老罗大战王自如”,其实还有些紧张。 期待的原因是看了王自如的视频后,理性和感性上...
    cZmLxD阅读 996评论 22 6