布局类
-
iOS用的是左手坐标系,z轴正方向指向屏幕外,x轴正方向屏幕从左向右,y轴正方向屏幕从上到下(右手坐标系将z轴方向取反即可)。后续的定位所用坐标系都是左手坐标系。
-
传统绝对定位frame
-
AutoLayout布局,借助封装好的SnapKit进行
- iOS 11后由于全面屏的出现,提供了SafeArea相关设置,可以让全面屏的布局避开刘海和底部系统横条区域,使交互更加友好。方法就是将AutoLayout的相对view改为SafeArea(但SafeArea不是UIView)
- Autolayout动画:必须要在更新uiview约束后,在UIView.animate的block中调用父view的layoutifneeded,动画才会生效
-
关于UIView的frame,bounds,center之间的区别和联系
- center是view中心点在父view坐标系中的坐标
- frame和bounds都是CGRect类型,包含坐标和长宽
- frame.origin是a在其父view坐标系中的坐标
- 通常bounds.size都是零,bounds.origin是a的左上角点的坐标值(相对于自身坐标系),只改变bounds(setBounds方法)中的origin并不会影响a在其父view中的位置,但会影响a中的子view在a中的位置,因为a的左上角不再是(0,0)。
- 只改变a的bounds.size(setBounds方法),会使a以它的center为中心向上下左右缩小或放大,同时,a的frame会发生改变,因为a在它父view中的位置发生了变化。但a的子view的bounds和frame都没有发生变化,从视觉上看,a的子view相对于a的位置没有变化。
- bounds.size的改变要优先于bounds.origin改变进行处理
- 如果要更改view的layer anchorPoint,最好让view使用绝对定位frame,不要用Autolayout,如果一定要用Aotulayout,需要注意更新约束才能使view的位置不发生变动。可以参考这里的例子来做
- 设置Autolayout的UIView的
translatesAutoresizingMaskIntoConstraints
为true,然后就可以设置改view的frame,使其用绝对定位的方式定位,不过这会引起警告。
-
关于CALayer的position,anchorPoint之间的联系和区别
- position是子layer在父layer坐标系中的位置
- anchorPoint的作用是用来给layer做变换用的,取值为[0,1],默认通常是(0.5,0.5)
- 二者的计算公式:
- position.x = frame.origin.x + anchorPoint.x * bounds.size.width;
- position.y = frame.origin.y + anchorPoint.y * bounds.size.height;
-
UIView和CALayer的区别
- UIView继承自UIResponder,是用来进行交互响应的对象。
- CALayer继承自NSObject,CoreAnimation中用来渲染图层的对象。
- 二者的职能不一样,但是UIView中实现了CALayerDelegate,这意味着它默认管理着一个layer,你可以更改此layer的frame、background等来改变此UIView的frame、背景颜色等。
- CALayer可以添加子layer,用来实现同一个view的多个图层叠加,实现不同效果,比如阴影圆角等效果。
- UIView也可以添加子view,用来实现不同view的层级关系,展示不同的布局。
-
clipToBounds和masksToBounds的区别
- clipToBounds是UIView的属性,若为true,该view的子view不可超出该view的边界,超出部分会被剪切掉。
- masksToBounds是CALayer的属性,若为true,该layer的子layer不可该layer的边界,超出部分会被剪切掉。
- 值得注意的是,当clipToBounds被设为true时,无论masksToBounds是否为true,该view的layer的子layer如果超出view的边界都会被裁剪掉。
- 基于此,如果要给view设置阴影发光效果,并且该效果不是设在UIView的顶层layer,必须保证顶层layer的masksToBounds和view的clipToBounds都为false,否则阴影等效果无效。因为阴影效果会超出bounds边界。此外,如果layer的背景色为clear,此layer的阴影也不会显示。下为阴影设置在顶层layer上:
extension UIView { func dropShadow() { layer.masksToBounds = false layer.shadowColor = UIColor.black.cgColor layer.shadowOpacity = 0.5 layer.shadowOffset = CGSize(width: -1, height: 1) layer.shadowRadius = 1 // 以下为性能考虑 layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath layer.shouldRasterize = true layer.rasterizationScale = UIScreen.main.scale } }
- ImageView的CALayer稍有不同。
- ImageView的图像是以CGImage的形式存储在layer的contents属性上。
- ImageView对layer设置圆角必须设置masksToBounds为true,否则图片无法裁剪为圆角。
-
有关应用生命周期的问题,另有单独一篇文章,见这里。
语言基础类
-
swift
- 值类型:(U)Int(Int8, Int16, Int32, Int64),Float,Double,Bool,tuple,可选值, 字符(串)、集合(数组,集合,字典), 结构体,枚举。Array、String、Dictionary、Set这些数据类型都是采用结构体来实现的。
- 引用类型:class
- 元组(tuple)适用于比较简单的混合类型,如果比较复杂还是要使用struct。
- Set集合支持4类数学运算,分别为intersection(交集)运算、symmetricDifference(交集的补集)运算、union(并集)运算和subtracting(补集)运算。
- 只有存储属性可以设置属性监听器(willSet/didSet),计算属性不可以。
- 便利构造方法最终要调用指定构造方法。
- 类实例的构造可能失败,使用init?定义的构造方法允许构造失败。
- 如果某个构造方法必须被子类实现,则可以使用required修饰。
- 构造方法安全检查: · 检查1:子类的指定构造方法中,必须完成当前类所有存储属性的构造,才能调用父类的指定构造方法。此检查可以保证:在构造完从父类继承下来的所有存储属性前,本身定义的所有存储属性也已构造完成。 · 检查2:子类如果要自定义父类中存储属性的值,必须在调用父类的构造方法之后进行设置。此检查可以保证:子类在设置从父类继承下来的存储属性时,此属性已构造完成。 · 检查3:如果便利构造方法中需要重新设置某些存储属性的值,必须在调用指定构造方法之后进行设置。此检查可以保证:便利构造方法中对存储属性值的设置不会被指定构造方法中的设置覆盖。 · 检查4:子类在调用父类构造方法之前,不能使用self来引用属性(基类无所谓)。此检查可以保证在使用self关键字调用实例本身时,实例已经构造完成。
- 扩展:
- 可以添加计算属性。
- 协议:
- 协议可以被类、结构体等数据类型遵守。如果开发者需要使某个协议只能被类遵守,可以使用class关键字来修饰。
- 如果需要协议中约定的属性或者方法是可选实现的,则可以将其声明为optional类型的,同时,需要将整个协议用@objc关键字修饰。
- 协议与扩展结合,可以为某个协议提供一个扩展,在扩展中为其约定的属性方法提供一套默认的实现。类似于公共方法。
- 如果需要对数据进行归档,需要数据遵守NSCoding协议,其中包含一个构造器方法(归档)和encode()方法(解归档),但归档不等于持久化,若是持久化需要NSKeyedArchiver类的archiveRootObject方法,将归档的数据一并写入磁盘文件。
-
typedef的用法:用来给一个类型起别名
typedef void(^SerchCompletionBlock)(SearchFilterDataModel *);
typedef int(^sumBlock)(int, int);
typedef struct A AA;
typedef struct A {
int age;
char *name;
} AA;
typedef enum B BB;
typedef enum B {
} BB;
-
swift 几种访问权限:
- private,只能本类中访问。
- fileprivate,只能本文件内访问。
- 默认,internal,只能本module中访问、继承、重写。
- public,可跨module访问,但不能继承和重写。
- open,可跨module访问、继承、重写。
-
optional的原理: 其实就是把变量包在enum中,有两个值,一个nil,一个变量本身的类型值。 解包方式有guard、if let、!强制解包、?? 赋予默认值、?链式访问。
-
weak和unowned:都是用来解决循环引用问题
- 无主引用与弱引用的最大区别在于,无主引用总是假定属性是不为nil的,如果属性所引用的实例被销毁释放了,再次使用这个实例程序会直接崩溃。而弱引用则允许属性值为nil,如果属性所引用的示例被销毁释放了,此属性会当成Optional值nil来处理,不会崩溃。两者相比,弱引用更加兼容,无主引用更加安全。
-
逃逸和非逃逸性闭包:闭包生命周期不同
- @escaping: 意味着闭包的调用是不确定的,比如异步请求调用闭包。
- @noescaping:闭包与调用的它的函数有相同的生命周期,二者同时结束。
-
defer关键字:修饰代码块,注意它优先作用于它所在的第一级代码块。
- 作用是在当前作用域退出之前执行。
- 比如作用域中有加锁操作,为避免忘记解锁,可以将解锁代码放在defer代码块中。
- 同一个作用域中如果有多个defer,从后往前执行,相当于栈
-
class和static关键字:都能修饰静态类/结构体/枚举的方法、属性
- class 修饰的方法可以被子类重写,但是可以加final关键字防止重写。不能修饰存储属性。
- static 修饰的方法不可以被子类重写。
- 如果没有重写需求,建议用static。
-
Any/AnyObject/AnyClass:
- Any:最广泛的代表,可以代表类实例、方法、enum、struct等等。
- AnyObject: 只能代表类的实例。
- AnyClass: 就是AnyObject.Type,代表类的元类型。
-
NSNotification/NSNotificationCenter/NSNotificationQueue:
- NotificationCenter.default.addObserver可以使用block或者selector
- NotificationCenter.default.post是同步通知,直到监听者收到通知执行后才会继续当前线程代码执行。
- NotificationQueue:有三种模式
-
typedef NS_ENUM(NSUInteger, NSPostingStyle) { NSPostWhenIdle = 1, // 当runloop处于空闲状态时post NSPostASAP = 2, // 当当前runloop完成之后立即post NSPostNow = 3 // 立即post,同步, 与不用queue的cente.default效果相同 };
-
-
KVC - KeyValueCoding
- 使用场景:通过kvc获取一些对象的属性,比如iOS13之前searchBar获取它的textField:
[searchBar valueForKey:@"_searchField"];
- 使用场景:通过kvc获取一些对象的属性,比如iOS13之前searchBar获取它的textField:
-
KVO - KeyValueObserve
- 使用场景:AVPlayerItem中监听播放状态、进度等。
layerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil) playerItem.addObserver(self, forKeyPath: "status", options: .new, context: nil)
框架类
Fundation
UIKit
- 如何自定义一个手势,使其自定义双击时间间隔?
class UIShortTapGestureRecognizer: UITapGestureRecognizer {
let tapMaxDelay: Double = 0.3
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
super.touchesBegan(touches, withEvent: event)
delay(tapMaxDelay) {
// Enough time has passed and the gesture was not recognized -> It has failed.
if self.state != UIGestureRecognizerState.Ended {
self.state = UIGestureRecognizerState.Failed
}
}
}
}
class func delay(delay:Double, closure:()->()) {
dispatch_after(dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), closure)
}