Swift实用100Tips--错误&要点
不管生活如何,还是要不停学习。
一年前的我一直在写OC,对底层了解不是很深刻,17年开始一直写swift,一开始在项目中遇到了各种困难,没有办法,只能克服。这篇文章的核心是呈现在在真实的工作中遇到的点,虽然有些简单的令人发指,不过走一遍应该会有些感悟。加油。
1.三目运算符
三木运算符号 中的 ?前面一定有一个空格。这是 ? 因为要区分 是 三目 还是 optional~ 如下:
let f = true
f? 2 : 3 //错误
f ? 2 : 3 //正确
2.报错:this class is not key value coding-compliant for the key... 这个报错 可能是因为 xib 里面的链接有错误 或者是 自定义的类中没有实现对应的属性值。
3.懒加载
懒加载在swift中很常用,一定要注意格式,要不然提示的错误可能有点找不到北。。
private var unityview:UnityAppController = {
let ua = UnityAppController()
ua.startUnity(UIApplication.sharedApplication())
return ua
}() // 注意后面一定有一个 括号
4.====
extension 在swift中经常用来分离代码,不过需要注意的是在swift 的extenion中是 不能 定义一个存储型变量的,存储型变量和计算性变量如果不熟悉可以去先研究一下这里,和OC中的属性是不同的概念。
5.控件初始化
使用懒加载的方式加载出来的控件 例如上面 3.懒加载里面的截图所示,得到的控件初始化的过程是只会执行一次。
6.用didSet 设置一个button的控制状态
如按钮点击后对应的状态有所改变
var seletedBtnView :DiscountBtnView? {
didSet{
oldValue?.btn?.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
oldValue?.label?.textColor = UIColor.blackColor()
oldValue?.btn?.layer.borderColor = UIColor.init(bd_hexColor: "F99C35").CGColor
oldValue?.btn?.backgroundColor = UIColor.whiteColor()
seletedBtnView?.btn?.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
seletedBtnView?.label?.textColor = UIColor.init(bd_hexColor: "F99C35")
seletedBtnView?.btn?.layer.borderColor = UIColor.init(bd_hexColor:"F99C35").CGColor
seletedBtnView?.btn?.backgroundColor = UIColor.init(bd_hexColor:"F99C35")
}
}
7.swift数组获取index
swift在这里确实比OC有那么一点麻烦,我的方法不一定是最好的。。
swift 3
let person1 = Person(name: "John")
let person2 = Person(name: "Sue")
let person3 = Person(name: "Maria")
let person4 = Person(name: "Loner")
let people = [person1, person2, person3]
let indexOfPerson1 = people.index{$0 === person1} // 0
let indexOfPerson2 = people.index{$0 === person2} // 1
let indexOfPerson3 = people.index{$0 === person3} // 2
let indexOfPerson4 = people.index{$0 === person4} // nil
8.swift 里面 除法可能需要这么个形式
CGFloat(index!).truncatingRemainder(dividingBy: 3)
9.automaticallyAdjustsScrollViewInsets
默认是 true 这个在OC里面很多时候就有些作祟,所以还是写出来没准布局有问题就是因为这货。
10.swift中的init方法的初始化问题
这个如果是自己定义的init方法需要注意完整的初始化。如果有需要我这里有文档而已提供。
11.初始化闭包的时候要看好了对应的括号。。
var mapMoveClosure:(() -> ())? // 这么写 注意 有一个括号
12.调用marsony动画 需要使用的是:
[UIView animateWithDuration:0.25 animations:^{
[self.pickerView layoutIfNeeded];
}];
13.有xib的视图(controller) 如果在viewdidload中使用了 width = self.view 会被布局成 width = 600 。 用了 xib 再使用 marsony布局需要注意了。 这个需要敲黑板了!!找了半天~(这个是在swift2.3项目中,3.0没测试)
14.我们使用的handyJson解析json数据。在使用中如果遇到了系统的关键字可以使用如下方式替换:
handyJSON 使用 遇到系统的字段 可以使用
mutating func mapping(mapper: HelpingMapper) {
mapper.specify(property: &ddfault, name: "defalut")
}
我没使用过swiftyJson😅.
15.获取当前显示的(最上层的)ViewController
static func getVisibleViewController() -> UIViewController? {
guard let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController where rootVC.childViewControllers.count>0, let navigationVC = rootVC.childViewControllers[0] as?UINavigationController else {
return nil
}
let currentVC = navigationVC.visibleViewController
guard let index = currentVC?.tabBarController?.selectedIndex where rootVC.childViewControllers.count>index+1,let currentNav = rootVC.childViewControllers[index] as?UINavigationController else {
return nil
}
return currentNav.visibleViewController
}
16.递归
写递归的时候 一定要注意结束的标志 只要有一个return 就直接全部都返回了
注意里面的 if let sub = findsubView 这个条件是 找到了才进行 return 原来的可能直接就返回了 nil。
另外递归最好是用 尾递归的方式,喵神的《swift 100 Tips》里面的最后章节有讲,没有这个书可以找我要。🤣
17.DispatchQueue.global
DispatchQueue.global(qos: .default).async { //swift 3 现在换成这种方式进行调用global
}
18.创建一个 队列 添加一个 item 具有 cancel功能
这个以前是Operation专属,现在GCD也可以这么干啦。~
let queue = DispatchQueue(label: "zhangTest1")
let workItem = DispatchWorkItem(qos: .default, flags: .assignCurrentContext) {
print("workItem")
}
queue.asyncAfter(deadline: DispatchTime.now() + time, execute: workItem)
workItem.cancel()
print(workItem.isCancelled)
19.delay方法&取消的方式
import UIKit
typealias task = (_ cancel : Bool) -> Void //返回一个这样的block
class Tools {
static func delay(time : TimeInterval, block : @escaping () -> ()) -> task? {
func delay_func(dblock : @escaping () -> ()) { //真正执行的func
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time, execute: dblock)
}
var result: task?
var closure : (() -> Void)? = block // 重复调用task(false)会有问题。
let delayedClosure : task = { cancel in // 使用带有 cancel 的方法进行包裹。
if let c = closure {
if cancel == false {
c()
}
}
closure = nil
result = nil // 如果提前结束 这里面也是nil了 如果不是提前结束 那么清理任务 方便下一次执行。
}
result = delayedClosure // 可选形 这个是因为提前取消了可能会产生nil 也正好不用执行了。。
delay_func {
if let delayedClosure = result {
delayedClosure(false)
}
}
return delayedClosure
}
static func cancel(t : task) { // 外部不要直接调用 t(true) 或者 t(flase) 这样很危险。
t(true)
}
}
let delayTask = Tools.delay(time: 2) {
print("exe")
}
Tools.cancel(t: delayTask!) //可以取消的~
20.想用 Swift 来实现 KVO
需要做额外的工作,那就是将想要观测的对象标记为 dynamic。
class MyClass: NSObject {
dynamic var date = Date()
}
//如果是 不能回去源代码的可以使用重写的方式。
class MyClass: NSObject {
var date = Date()
}
class MyChildClass: MyClass {
dynamic override var date: Date {
get { return super.date }
set { super.date = newValue }
}
}
(三方: Observable-Swift 如果需要可以试试)
21.字符串转换到 UnsafeMutablePointer<Int8>
let name = "classvarB"
let greeting : UnsafeMutablePointer<Int8> = strdup(name)
print(class_getInstanceVariable(classTestB.self, greeting))
22.var 和可选型是 没有关系的。 这个一定要注意。
23.书写控件的时候尽量使用懒加载的形式 注意 懒加载的时候 控件属于强引用 removeFromsuperView 不会释放。页面消失的时候进行释放。
24.当viewcontroller里面引用了 一个网络请求。 网络请求里面有一个回调(CSChooseGoodNetWork),回调 闭包 而且这里面添加上了 @escaping 调用的类 CSChooseGoodView 里面进行调用的时候 里面所有的 fileprivate 和 private 都需要去掉才能使用。
25.直接创建一个 枚举值 GoodsPromotionType.init(rawValue: type)
enum GoodsPromotionType:String {
case limiteDiscount = "1" //限时折扣
case discount = "2" //满件折扣
case fullDisReduction = "3" //满减
case fullReduction = "4" //一口价
}
26.添加上 dynamic 可以使用 OC 的method swizzle方法进行方法交换
参考
27.class func (类方法中)中不能 使用非 class 修饰的func(实例方法)
28.模拟器和真机
模拟器和真机的 区别
#if arch(i386) || arch(x86_64) //模拟器
#else //真机
var traceInstance: BTRACE? //比方说 初始化一个电子围栏服务
#endif
29.自动框架库Snapkit 里面 使用 snp update 不管用?? remake 才可以~ 这个还没找原因。
30.private func 这个方法 不能用在 tap手势上(多么痛的领悟。。), 不能多个控件添加 一个手势。
31.错误 Argument of '#selector' does not refer to an initializer or method 原因是 selector 里面的 不能写成 method() 有括号就错了(有括号不就是调用了么~)
32.截屏的代码
UIGraphicsBeginImageContext(size)
let text = UIGraphicsGetCurrentContext()!
self.view.layer.render(in: text)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
33.页面逻辑(侧滑)代码
func panSliderView(panGes:UIPanGestureRecognizer) {
let translation = panGes.translationInView(self.view)
let centerX = panGes.view!.center.x + translation.x
let another :CGFloat = sliderLeftMargin + (ScreenWidth - sliderLeftMargin) / 2.0
//限制不能超过边线。
if centerX >= another {
panGes.view?.center = CGPointMake(centerX, panGes.view!.center.y)
}
panGes.setTranslation(CGPointZero, inView: self.view)
if (panGes.state == .Ended && (centerX != another) && (centerX != sliderLeftMargin + sliderWidth)) { //判断一下中心店的距离 再做相应的动画。
if centerX < sliderLeftMargin + sliderWidth {
//向左移动
viewAnimation(slideView,frame: leftFrame)
} else {
//向右移动
hiddenSlider()
}
}
}
34.pod
pod install --verbose --no-repo-update 这个pod命令可以添加那些非本地的
pod update --verbose --no-repo-update
pod 'EaseUI', :path => '../EaseUI' // pod 安装本地库.
pod update 报错
zsh: /usr/local/bin/pod: bad interpreter: /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin: no such file or directory
解决:
sudo gem uninstall cocoapods
sudo gem install -n /usr/local/bin cocoapods
pod 只更新新的 原来的就不更新了。
pod update --verbose --no-repo-update
sudo gem install -n /usr/local/bin cocoapods --pre
35.xib中 commend + option + 加号 快速 update frame。
36.寻找第一响应者
extension UIView {
func getCurrentFirstResponder() -> UIView? {
for subView: UIView in self.subviews as [UIView] {
if subView.isFirstResponder() {
return subView
}
else {
if let sub = subView.getCurrentFirstResponder() {
return sub;
}
}
}
return nil
}
}
37.swift3.0 改变了许多的语法 例如 NSDate 变成了 变成了建议使用 Date,那么扩展里面是不能通用的 会找不到方法,2.3版本升级的时候需要注意。
38.xib 上面添加了 一个手势 要注意 加载的时候写的是 first 还是 last ,另外没有 file owner 绑定的时候 加载的fileowner 可以写成nil
39.原来关键字可以这么使用啊
var `default` = 2
func test() {
print(`default`)
self.default = 4
}
40.枚举可以这么写
enum Section: Int {
case input = 0, todos, max
}
42.switch可以这样写
switch sender.tag {
case 1,2:
order = sender.tag
.....
}
43.数组的 contains 方法是比较的 哈希值 所以 “1” 这样的对象可以直接 比较,不必在乎是不是同一个对象。
44.reduce 方法
let arr = [1,2,3,4]
arr.reduce("") { (result, intt) -> String in
return result + "\(intt)"
}
// 初始值 中间结果 中间的添加因子 返回的结果
arr.reduce([]) { (result, inttt) -> [String] in
return result + ["\(inttt)"]
}
arr.reduce([]) { (result, inttt) -> [Int] in
print(result)
print(inttt)
return result + [inttt]
}
45.OC里面 很多options里面的参数是枚举使用 | 连起来,表示同时满足,swift里面是这样的:
self.thumbBtn.setTitle(mappedObject.datas?.praise_num, forState: [UIControlState.Normal,UIControlState.Selected]) //数组表示。
46.使用网页打开应用
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>CSC</string>
<key>CFBundleURLSchemes</key>
<array>
<string>CSCMall</string>
</array>
</dict>
</array>
调起方法 CSCMall://CSC
47.插入视图的方法:
self.view.insertSubview(scrollView, atIndex: 0); 可以是这样用的用来控制层级,这个方法尽量别在tableView的cell上或者能滑动View上使用,因为卡!
48.collectionview 转换卡片和 列表。
private func setHorizionLayout() {
let layout = UICollectionViewFlowLayout()
// 设置最小行间距 10
layout.minimumLineSpacing = 10;
// 设置每个cell之间间距
layout.minimumInteritemSpacing = 0;
// 设置每一组的间距,距离顶部的距离
layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5)
layout.itemSize = CGSizeMake((ScreenWidth - 20.0)/2, (ScreenWidth - 20.0)/2+68)
collectionView.collectionViewLayout = layout
}
//设置一行一个的。类似tableView的情况
private func setVerticalLayout(){
let layout = UICollectionViewFlowLayout()
// 设置最小行间距 10
layout.minimumLineSpacing = 10;
// 设置每个cell之间间距
layout.minimumInteritemSpacing = 0;
// 设置每一组的间距,距离顶部的距离
layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5)
layout.itemSize = CGSizeMake(ScreenWidth-10.0, 125)
collectionView.collectionViewLayout = layout
}
可以设置 collectionView.collectionViewLayout = layout 用于视图转换, 这里面的东西可以用来 转换列表和卡片视图。
49.使用 try catch
var s : Reachability?
do {
try s = Reachability.init(hostname: "http://www.baidu.com")
} catch {
switch (error)
print("Reachability 创建 init 出错。")
print(error) //获取的error
}
50.** 设置 nav上面的 right left buttonItem**
portraitUserVc.navigationItem.rightBarButtonItem?.setTitleTextAttributes([NSFontAttributeName:UIFont.systemFontOfSize(16),NSForegroundColorAttributeName: UIColor.orangeColor()], forState: .Normal)
51.使用for-in 进行循环遍历. ---> 另外纯遍历可以使用foreach
for (index,element) in orderGoodsList.enumerate() { //这个方法不错, 可以舍弃 for i in 0..<5 的形式了。
detailView.idItem = element
}
52.注意 桥接文件里面的数据,相当于每个文件包含了一次。可能会造成冲突。(三方库里面有不少一样的名字,那么废了~)
53.复制到粘贴板
let pasteboard = UIPasteboard.generalPasteboard()
pasteboard.string = orderCodeLabel.text
SVProgressHUD.showSuccessWithStatus("已复制到粘贴板") // 复制到粘贴板~
54.automaticallyAdjustsScrollViewInsets = false 这货在OC里面就作祟很久了。
55.创建控件的时候 同时创建了一个闭包。 这时候需要注意里面的 self。另外, 这里的 snp_makeConstraints 是调用方法,tapBackClosure是一个存储属性。
lazy var messageReason : ZMReFundMessageReasonView = {
let v = NSBundle.mainBundle().loadNibNamed("ZMReFundMessageReasonView", owner: self, options: nil)?.first as! ZMReFundMessageReasonView
self.view.addSubview(v)
v.snp_makeConstraints { (make) in
make.top.equalTo(self.goodsStatus.snp_bottom).offset(1)
make.left.right.equalTo(self.view)
make.height.equalTo(self.view).multipliedBy(0.08)
}
v.tapBackClosure = {[unowned self] in //点击了出现原因的。
self.alertActionSheet.showInView(self.view)
}
return v
}()
// 这个我也有点忘记是咋回事了,回头再看看~
56.enum 携带变量的形式
enum Number1 {
case one(count:Int,string:String)
case two(count:Int)
}
var num1 = Number1.one(count: 5,string: "zhang") //枚举携带变量的形式
var num2 = Number1.two(count: 3)
switch num2 {
case .one(let count,let name): //这个地方主要要有一个let
print(count)
print(name)
case let .two(count): //只有一个常量可以卸载 let 可以写到前面。
print(count)
default:
print("defalut")
}
57.中文转换成拼音, 用于检索。(例如中文按照拼音排序)
let personArray = [Person(name: "zhang"),Person(name: "han")]
let sortArray = personArray.sorted { (p1, p2) -> Bool in //直接排序有问题。
p1.name > p2.name
}
func transForm(chinese:String) -> String {
let mutableStr = NSMutableString(string: chinese) as CFMutableString
var cfRange = CFRange.init(location: 0, length: 0)
// kCFStringTransformStripCombiningMarks 去掉重音符号
let _ = CFStringTransform(mutableStr, nil, kCFStringTransformToLatin, false)
let _ = CFStringTransform(mutableStr,nil, kCFStringTransformStripCombiningMarks, false) // false 是反转的意思
// 这个地方为什么能传nil ?? 又是为什么这里 (location: 0, length: 0) 还能使用啊。~?
let _ = CFStringTransform(mutableStr, &cfRange, kCFStringTransformStripCombiningMarks, false)
// print(mutableStr)
return mutableStr as String
}
transForm(chinese: "张")
// 这样增加了 trans的使用次数
let sorta2 = personArray.sorted { (p1, p2) -> Bool in
return transForm(chinese: p1.name) > transForm(chinese: p2.name)
}
// 排序的具体流程可以这样 中途保存了 原始的中文名称
let sort3 = personArray.map { (p) -> (String, String) in
return (p.name, transForm(chinese: p.name))
}.sorted { (t1, t2) -> Bool in
return t1.1 < t2.1
}.map { (t) -> String in
t.1
}
// 简化版本
let sort4 = personArray.map { //转换中文
transForm(chinese: $0.name)
}.sorted()
58.@warn_unused_result 写sdk的时候 可以去掉返回值没有调用的警告
59.lipo命令合并和拆分IOS静态库
具体参考
60.判断是不是中文的。
- (BOOL)isChinese
{
NSString *match = @"(^[\u4e00-\u9fa5]+$)";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF matches %@", match];
return [predicate evaluateWithObject:self];
}
61.限制一个 swift 类是controller 又满足了一个协议。
class func requestNewData<T:NewGoodsSellNetWorkProtocol where T:UIViewController>(controller:T) {
}
如果多个方法都需要使用这个泛型 , 那么就定义在类上
class RequestNewGoodsSell<T:NewGoodsSellNetWorkProtocol where T:UIViewController> {
62.@objc protocol NewGoodsSellNetWorkProtocol 添加protocol 中的方法为#selector的时候需要在前面添加上@objc
63.在iOS 8以后的版本中 GCD 变化比较大,time 直接可以用 TimeInterval 类似可以这样:
let time: TimeInterval = 2.0
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {
print("2 秒后输出")
}
DispatchWorkItem创建出来的item是可以实现取消的。
let item = DispatchWorkItem {
print("1235")
}
let myQueue = DispatchQueue(label: "我的线程")
myQueue.async(execute: item) //这个便执行了 对应的block
item.cancel() // 这里可以实现取消。
DispatchQueue.main.async(execute: item)
//另外,在playground中 貌似 DispatchQueue.main.asyncAfter 这个方法是不管用的。playground貌似不支持 DispatchQueue.main
··
64.获取类名
//swift 2.3
static var Identify:String{
return (String.init(UTF8String: object_getClassName(self)) ?? "").componentsSeparatedByString(".").last!
}
//swift 3.0
var className: String {
return String.init(cString: class_getName(object_getClass(self))) //获取类的名字
}
65.Mirror 可以实现Struct 的子类遍历(children). 这个可以看看 HandyJson的相关代码。
66.关联类型 E 可以是满足一个协议的泛型,这个协议在实现中也是必须要声明成正确的类型。
protocol Rle:Ele {
}
protocol CommandProtocol {
associatedtype E : Ele
func on()
}
class P : Ele {
}
class CommandCondtion:CommandProtocol {
typealias E = P
func on() {
print("1")
}
}
额.....说好的 100 个呢?? 好吧。其他的有的是让我合并了,有的是我的低级失误,我真的写了100个!放一个图片为证:
这里还会不停的更新!
有问题请QQ:645360439。
不开心的时候就努力学习,加油!
最后更新是 2018.01.19。