Swift实用100Tips--错误&要点

作者: 韩大熊宝要姓张 | 来源:发表于2018-01-19 09:52 被阅读220次

不管生活如何,还是要不停学习。

一年前的我一直在写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个!放一个图片为证:

100个

这里还会不停的更新!

有问题请QQ:645360439
不开心的时候就努力学习,加油!
最后更新是 2018.01.19。

相关文章

网友评论

    本文标题:Swift实用100Tips--错误&要点

    本文链接:https://www.haomeiwen.com/subject/xtebnxtx.html