移动周分享-第45期

swift 的 guard & defer - 杨志平

guard 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 原始方法 (强解包可能会出现问题)
func tranIntToString1(x: Int?) -> String {
if x == nil || x! <= 0 {
// 不符合值的要求时,写点代码
return ""
}
// 使用x
return x!.description
}


// 改进
func tranIntToString2(x: Int?) -> String {
if let x = x where x>0 {
return x.description
}
return ""
}

// 保镖模式
// 和上面写法对比
func tranIntToString3(x: Int?) -> String {
guard let x = x where x > 0 else {
return ""
}
// 变量不符合条件判断时,执行下面代码
return x.description
}


// 非可选型
func tranIntToString4(x: Int) -> String {
guard x > 0 else {
return ""
}
return x.description
}


// 常常用于条件判断拦截
var view = UIView(frame: CGRectMake(0,0,80,80))
view.backgroundColor = UIColor.redColor()

UIView.animateWithDuration(0.3) { [weak view]() -> Void in
guard let view = view where view.alpha>0 else {return}
view.alpha = 0
}

懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//1.分析 NSArray 是一个闭包的返回值,而这是一个没有参数的闭包 
lazy var dataArray:NSArray = { [] }()

//2.也可以写成这样
lazy var dataArray:NSArray = { return NSArray() }()

//3.从plist文件加载
lazy var dataArray:Array<XWWine> = {
let winePath = NSBundle.mainBundle().pathForResource("wine.plist", ofType: nil)!
let winesM = NSMutableArray(contentsOfFile: winePath);
var tmpArray:Array<XWWine>! = []
for tmpWineDict in winesM! {
var wine:XWWine = XWWine.wineWithDict(tmpWineDict as! NSDictionary)
tmpArray.append(wine)
}
print("我就运行一次")
return tmpArray }()

调用的时候再在家初始化方法(懒加载)

1
2
3
4
lazy private var underlineView: UIView = {
let view = UIView(frame: .zero)
return view
}()

defer 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
defer 关键字
*/

postfix func ++(inout x: Int) -> Int {
defer {
x = x/2
defer {
x += 100
}
}
return x
}
//
//postfix func ++(inout x: Int) -> Int {
// let current = x
// x += 2
// return current
//}


var num = 100
let num2 = num++
num //150
num2 //100

prefix func ++(inout x:Int) -> Int {
x += 2
return x
}

var number = 100
let number2 = ++number
number // 102
number2 // 102

嵌套枚举使用(简直就是动态Model)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public enum MenuScrollingMode {
case ScrollEnabled
case ScrollEnabledAndBouces
case PagingEnabled
}
public enum MenuItemWidthMode {
case Flexible
case Fixed(width: CGFloat)
}
public enum MenuDisplayMode {
case Standard(widthMode: MenuItemWidthMode, centerItem: Bool, scrollingMode: MenuScrollingMode)
case SegmentedControl
case Infinite(widthMode: MenuItemWidthMode)
}

// 初始化:用来携带信息很不错
public var menuDisplayMode = MenuDisplayMode.Standard(widthMode: PagingMenuOptions.MenuItemWidthMode.Fixed(width: 44), centerItem: false, scrollingMode: PagingMenuOptions.MenuScrollingMode.PagingEnabled)

// 实例
func labelWidth(size size: CGSize, widthMode: PagingMenuOptions.MenuItemWidthMode) -> CGFloat {

switch widthMode {
case .Flexible: return ceil(size.width)
case let .Fixed(width): return width
}
}

iOS事件传递 & 事件响应 - 张超耀

什么是响应链

  • 在我们点击屏幕的时候,iphone获取到了用户进行了“单击”这一行为,操作系统把包含这些点击事件的信息包装成UITouch和UIEvent形式的实例,然后找到当前运行的程序,逐级寻找能够响应这个事件的对象,直到没有响应者响应。这一寻找的过程,被称作事件的响应链

什么是响应者

  • 在iOS中,能够响应事件的对象都是UIResponder的子类对象。UIResponder提供了四个用户点击的回调方法,分别对应用户点击开始、移动、点击结束以及取消点击,其中只有在程序强制退出或者来电时,取消点击事件才会调用
(void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event;
1
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet * _Nonnull)touches NS_AVAILABLE_IOS(9_1);
  • 我们可以看到方法接收两个参数,一个UITouch对象的集合,还有一个UIEvent对象。这两个参数分别代表的是点击对象和事件对象

事件对象

  • iOS使用UIEvent表示用户交互的事件对象,在UIEvent.h文件中,我们可以看到有一个UIEventType类型的属性,这个属性表示了当前的响应事件类型。分别有多点触控、摇一摇以及远程操作(在iOS之后新增了3DTouch事件类型)。在一个用户点击事件处理过程中,UIEvent对象是唯一的

点击对象

  • UITouch表示单个点击,其类文件中存在枚举类型UITouchPhase的属性,用来表示当前点击的状态。这些状态包括点击开始、移动、停止不动、结束和取消五个状态。每次点击发生的时候,点击对象都放在一个集合中传入UIResponder的回调方法中,我们通过集合中对象获取用户点击的位置。其中通过- (CGPoint)locationInView:(nullable UIView *)view获取当前点击坐标点,- (CGPoint)previousLocationInView:(nullable UIView *)view获取上个点击位置的坐标点。

  • 为了确认UIView确实是通过UIResponder的点击方法响应点击事件的,我创建了UIView的类别,并重写+ (void)load方法,使用method_swizzling的方式交换点击事件的实现 ——参照最下方demo

响应链传递

  • 上面已经介绍了某个控件在接收到点击事件时的处理,那么系统是怎么通过用户点击的位置找到处理点击事件的view的呢?
  • 存在着这么一个方法:- (nullable UIResponder *)nextResponder,通过方法名我们不难发现这是获取当前view的下一个响应者,逐级获取下一响应者,直到没有下一个响应者位置

源码地址