将多个信号链接在一起成为一组操作(a group of operations completes)可以使得更复杂的异步操作变得简单
// 在两个网络请求结束后打印信息
//
// +merge: takes an array of signals and returns a new RACSignal that passes
// through the values of all of the signals and completes when all of the
// signals complete.(太长了,求翻译)
//
// -subscribeCompleted: 在信号结束后调用
[[RACSignal
merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]]
subscribeCompleted:^{
NSLog(@"They're both done!");
}];
dispatch group 可以把并行执行的多个任务合成一组,于是调用者就可以知道这些任务何时才能全部执行完毕。例如,可以把一系列操作文件的任务或者网络请求的任务添加到 dispatch group 中,等全部完成这些耗时的任务之后再进行下一步操作。 创建 dispatch group 的方式如下:
dispatch_group_t dispatch_group_create(void);
dispatch group 就是个简单的数据结构,这种结构彼此之间没有什么区别,它不像派发队列,后者还有个用来区别身份的标识符。
taps,drags,swipes 通常都只涉及了一个 touch,比较简单去跟踪。但是处理由多个 touches 组成的触摸事件时,比较有挑战性。需要去记录 touch 的所有相关属性,并且改变它的 state 等等。需要做到两点:
Set the view’s multipleTouchEnabled property to YES;将多点触摸属性置为 YES;
Use a Core Foundation dictionary object (CFDictionaryRef) to track the mutations of touches through their phases during the event;用 CFDictionaryRef 来跟着 UITouch,这里用 CFDictionaryRef 而不是 NSDictionary,因为 NSDictionary 会 copy 它的 key。而 UITouch 没有采取 NSCopying 协议。
Determining when the last touch in a multitouch sequence has ended,判断 multitouch sequence 里的最后一个 touch 是否结束,可以用下面的代码
If the user touches inside A, it recognizes the touch. But if a user holds one finger inside view B and also touches inside view A, then view A does not receive the touch because it was not the only view tracking touches. Similarly, if a user holds one finger inside view A and also touches inside view B, then view B does not receive the touch because view A is the only view tracking touches. At any time, the user can still touch both B and C, and those views can track their touches simultaneously.
For a responder object to handle a touch, the touch’s view property must hold a reference to the responder. 一个 responder 对象想要处理一个 touch ,那么 touch 的 view 属性必须持有这个 responder。
事件的转发经常需要去分析 touch 对象觉得它是否应该转发事件。这里有一些方法你可以采取去分析:
With an “overlay” view, such as a common superview, use hit-testing to intercept events for analysis prior to forwarding them to subviews.(使用 overlay view,例如公用的父视图,在转发到 subviews 之前拦截事件去分析)
Override sendEvent: in a custom subclass of UIWindow, analyze touches, and forward them to the appropriate responders.(UIWindow 的子类重载 sendEvent: 方法,将事件转发到合适的 responders)
You should never retain an event object or any object returned from an event object. 不要持有事件对象或者从事件 对象中返回的对象。因为 UIEvent 对象在多点触摸序列(指手指触摸屏幕到离开屏幕)中是持久的, UIKit 会重用 UIEvent 对象,如果你需要持有 event 或者 touch 的相关信息时,你可以拷贝相关信息,赋值给相关变量。
The UIResponder class defines an interface for objects that respond to and handle events. It is the superclass of UIApplication, UIView and its subclasses (which include UIWindow). Instances of these classes are sometimes referred to as responder objects or, simply, responders. 它定义了响应和处理事件的接口。像一些能够处理相应事件的类(UIApplication, UIView 等)都是它的子类。
event。代表这个事件 event 的所有 touches ,所以上面的 touches 也属于它。This differs from the set of touches because some of the touch objects in the event may not have changed since the previous event message. 它跟上面的 touches 的区别就在于它可能包含发生改变的 touch 。强调一个状态的改变。
管理响应链的方法
nextResponder
Returns the receiver's next responder, or nil if it has none. 接受者的下一个响应者,如果没有的话就为 nil。nextResponder 就是响应链中下一个处理事件的对象。
Returns a Boolean value indicating whether the receiver is the first responder. 是否是第一响应者。默认为 YES。
canBecomeFirstResponder
Returns a Boolean value indicating whether the receiver can become first responder. 是否能够成为第一响应者。默认为 NO。
如果返回 YES ,就说明它能够成为第一响应者,it becomes the first responder and can receive touch events and action messages.能够接受触摸事件和动作消息。子类如果想要成为第一响应者,那么必须重载这个方法。注意,你只有当 view 已经添加到 view 层级里面才能发送这个消息(becomeFirstResponder),不然这个结果是不确定的,例子如下:
Note: Make sure that your app has established its object graph before assigning an object to be the first responder. For example, you typically call the becomeFirstResponder method in an override of the viewDidAppear: method. If you try to assign the first responder in viewWillAppear:, your object graph is not yet established, so the becomeFirstResponder method returns NO. 注意:在一个对象成为 first repsonder 之前要确保建立好 object graph。例如,你通常在 viewDidAppear: 方法里面调用 becomeFirstResponder。如果 viewWillAppear: 方法里面设置 first responder,这个时候 object graph 还没建立好,所以 becomeFirstResponder 会返回 NO。
becomeFirstResponder
Notifies the receiver that it is about to become first responder in its window. 报告接受者它将要在 window 上成为为第一响应者。默认返回 YES。
A responder object only becomes the first responder if the current responder can resign first-responder status (canResignFirstResponder) and the new responder can become first responder. 只有当前的响应者能够辞去第一响应者,新的响应者才能够成为第一响应者。也就是说第一响应者永远只有一个。
If the view’s window property holds a UIWindow object, it has been installed in a view hierarchy;if it returns nil, the view is detached from any hierarchy. view 的 window 属性持有 UIWindow 对象时才表示这个 view 已经添加到 view 层级中。也就说只有 view 层级确定成功后才能成为第一响应者。
canResignFirstResponder
Returns a Boolean value indicating whether the receiver is willing to relinquish first-responder status. 是否能够将要放弃作为第一响应者的状态。默认为 YES。
As an example, a text field in the middle of editing might want to implement this method to return NO to keep itself active during editing. 例如,编辑中的文本输入框可能想实现这个方法返回 NO 来保持自己的编辑状态(不过,这种情况目前还没有遇到过。)。
resignFirstResponder
Notifies the receiver that it has been asked to relinquish its status as first responder in its window.通知接受者它被询问是否放弃在 window 上作为第一响应者的状态。 默认为 YES。 注意:子类重载该方法的时候,必须实现父类的方法。
A major role of your app’s application object is to handle the initial routing of incoming user events. It dispatches action messages forwarded to it by control objects (instances of the UIControl class) to appropriate target objects. application 主要的职责是处理用户事件。
The UIControl class implements common behavior for visual elements that convey a specific action or intention in response to user interactions. UIControl 为响应用户的交互而对那些可见的元素实现了共同的行为,其实也是事件的高级处理。它使用来 Target-Action 机制向 APP 报告用户的交互。
UIControl 由 UIControlState 类型的属性 state 决定它的外观和支持用户交互的能力。
The control handles all the work of tracking incoming touch events and determining when to call your methods.处理所有的跟踪将要来的触摸事件的工作,并且决定什么时候调用你的方法。通过 addTarget:action:forControlEvents: 方法添加 target 和 action ,target 可以为任何对象,一般是包含 control 的 view controller,如果 target 为 nil,那么控件会通过响应链查找定义了该方法的响应者。
The control maintains a list of its attached targets and actions along and the events each supports.里面维持了一个数组来存储它的 target、action 已经所支持的事件。control 不会 retain target。可以参考iOS-Runtime-Headers _targetActions数组
This method iterates over the control’s registered targets and action methods and calls the sendAction:to:forEvent: method for each one that is associated with an event in the controlEvents parameter. 从 iterates 可以看出 UIControl 里面是维持了一个数组。
根据 UIControlEvents 来指定用户交互的特定形式,例如:UIButton 就是 UIControlEventTouchDown 或者 UIControlEventTouchUpInside 触发 action 方法,而 UISlider,则是 UIControlEventValueChanged。 When a control-specific event occurs, the control calls any associated action methods right away. Action methods are dispatched through the current UIApplication object, which finds an appropriate object to handle the message, following the responder chain if needed. For more information about responders and the responder chain, see Event Handling Guide for iOS. 当一个特定的事件发生时,control 就正确的调用相关的 action 方法。通过 UIApplication 对象(它能够找到相应的对象来处理消息)来分发 action 方法,如果需要的话,则通过响应链来找到。
Target-action is a design pattern in which an object holds the information necessary to send a message to another object when an event occurs. Target-action 是一种设计模式,当某个事件发生时,持有信息的对象发送消息给另外一个对象。持有的信息包括接受消息的对象以及消息。
When a user touches that view, the gesture recognizer receives a message that a touch occurred before the view object does. As a result, the gesture recognizer can respond to touches on behalf of the view. 当用户触摸 view 的时候,gesture recognizer 会在 view(靠 touchBegan、moved、ended、cancelled:withEvent:这几个方法来处理 touch 事件)之前收到这个 touch 事件。所以 gesture recognizer 能够代表 view 来响应这个 touch。
When a view has multiple gesture recognizers attached to it, you may want to alter how the competing gesture recognizers receive and analyze touch events. By default, there is no set order for which gesture recognizers receive a touch first, and for this reason touches can be passed to gesture recognizers in a different order each time. 如果一个 view 上有多个 gesture recognizer 时,你可能想改变这些竞争手势如何接受和处理触摸事件的。默认情况下,这些手势谁第一个接受到 touch 是无序的,所以手势可能每次都发生在不同的顺序。所以,我们可以用 delegate 和子类化来处理改变这些行为。
Specify that one gesture recognizer should analyze a touch before another gesture recognizer. (指定某个手势发生在另外一个手势前面)
Allow two gesture recognizers to operate simultaneously(允许两个手势同时发生).
Prevent a gesture recognizer from analyzing a touch(防止某个手势发生).
Note:You need to implement a delegate and return YES on only one of your gesture recognizers to allow simultaneous recognition. However, that also means that returning NO doesn’t necessarily prevent simultaneous recognition because the other gesture recognizer's delegate could return YES. 注意:实现一个代理,让它返回 YES,就能够允许同时发生。当然它也意味着返回 NO 不能防止它不同时发生,因为很有可能其他的代理返回 YES。
指定两个手势间的单程关系
子类重载canPreventGestureRecognizer:或者canBePreventedByGestureRecognizer:方法返回 NO 来处理。默认返回 YES。 例如,rotation 能够防止 pinch,而 pinch 不能防止 rotation,就可以用下面的代理来处理。
In iOS 6.0 and later, default control actions prevent overlapping gesture recognizer behavior.iOS6 以后,默认的 control actions 控制会阻止覆盖手势行为。例如,按钮的事件就是一个 tap 手势。如果你在按钮的父视图上添加了一个手势,那么当用户点击按钮的时候,按钮会收到响应触摸事件,它的父视图不会响应。当然这仅仅作用于默认有control action的手势识别上,它还包括:
tap 手势在 UIButton,UISwitch,UIStepper,UISegmentControl,UIPageControl;
swipe 手势在 UISlider;
pan 手势在 UISwitch;
当你想要重载 control 默认的 action,在 control 上面添加手势时,手势第一次收到触摸事件。但是你得注意了,需要去看看iOS Human Interface Guidelines以确保能够为用户提供直觉的体验(这样做是不推荐的,记得看view programming的时候看到不推荐在 UIButton 上面手势等之类的事件)。
![Apple Default delivery path for touch events][12]
手势首先识别 touch
A window delays the delivery of touch objects to the view so that the gesture recognizer can analyze the touch first. window 会延迟传递 touch 对象到 view,所以手势能够最先识别这个 touch。如果手势识别了这个触摸事件,那么 window 不会分发剩下到 view 上,并且还会取消之前发送出去的触摸事件。
例如,有一个离散的手势需要两根手指,所以有两个 touch 对象,传递流程如下图所示。
![Apple Sequence of messages for touches][13]
The window sends two touch objects in the Began phase—through the touchesBegan:withEvent: method—to the gesture recognizer. The gesture recognizer doesn’t recognize the gesture yet, so its state is Possible. The window sends these same touches to the view that the gesture recognizer is attached to.
The window sends two touch objects in the Moved phase—through the touchesMoved:withEvent: method—to the gesture recognizer. The recognizer still doesn’t detect the gesture, and is still in state Possible. The window then sends these touches to the attached view.
The window sends one touch object in the Ended phase—through the touchesEnded:withEvent: method—to the gesture recognizer. This touch object doesn’t yield enough information for the gesture, but the window withholds the object from the attached view.
The window sends the other touch object in the Ended phase. The gesture recognizer now recognizes its gesture, so it sets its state to Recognized. Just before the first action message is sent, the view calls the touchesCancelled:withEvent: method to invalidate the touch objects previously sent in the Began and Moved phases. The touches in the Ended phase are canceled.
Note: A touch object is associated with its hit-test view for its lifetime, even if the touch later moves outside the view. 注意,一个 touch 对象在 hit-test view 的生命周期内都跟它关联的,即使这个 touch 后面移动到它外面。
如果 initial object(hit-test view 或者 the first responder,它通常是个 view)处理不了这个事件,UIKit 会在响应链中将它传递给 next responder。每个响应对象决定它是否处理这个事件,还是通过调用nextResponder传递下去。 这个过程持续到 app object,如果 app object 都处理不了,那么就丢弃掉这个事件。
Important: If you implement a custom view to handle remote control events, action messages, shake-motion events with UIKit, or editing-menu messages, don’t forward the event or message to nextResponder directly to send it up the responder chain. Instead, invoke the superclass implementation of the current event handling method and let UIKit handle the traversal of the responder chain for you.
If your custom responder is a subclass of UIView or UIViewController, you should implement all of the event handling methods.(view 或者 viewcontroler 要实现所有的事件处理方法)
If you subclass any other responder class, you can have a null implementation for some of the event methods.(其他 responder 类的子类,可以不实现其中某个事件处理方法)
In all methods, be sure to call the superclass implementation of the method.(确保调用父类的实现方法)
NOTE: Universal links let iOS 9 users open your app when they tap links to your website within WKWebView and UIWebView views and Safari pages, in addition to links that result in a call to openURL:, such as those that occur in Mail, Messages, and other apps.
For users who are running versions of iOS earlier than 9.0, tapping a universal link to your website opens the link in Safari.
“How to support Universal Links?”
Step1:创建一个json 格式的apple-app-site-associatio 文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"applinks": {
"apps": [],
"details": [
{
"appID": "9JA89QQLNQ.com.apple.wwdc",
"paths": [ "/wwdc/news/", "/videos/wwdc/2015/*" ]
},
{
"appID": "TeamID.BundleID2",
"paths": [ "*" ]
}
]
}
}
根据 paths 键设定允许的路径列表, 或只是一个星号如果你想打开 APP 而不管路径是 什么
注意:paths 路径是大小写敏感的
NOTE:The website paths you specify in the paths array are case sensitive.”
upload it to the root of your HTTPS web server. The file needs to be accessible via HTTPS—without any redirects—at https:///apple-app-site-association. Next, you need to handle universal links in your app.
The NS_ENUM and NS_OPTIONS macros provide a concise, simple way of defining enumerations and options in C-based languages. These macros improve code completion in Xcode and explicitly specify the type and size of your enumerations and options. Additionally, this syntax declares enums in a way that is evaluated correctly by older compilers, and by newer ones that can interpret the underlying type information.