Uiresponder swift что это
iOS Responder Chain: UIResponder, UIEvent, UIControl and uses
What on earth is this «first responder» thing when I’m dealing with UITextFields?
Why do UIViews subclass things like UIResponder?
What’s the point of these?
In iOS, the Responder Chain is the name given to an UIKit-generated linked list of UIResponder objects, and is the foundation for everything regarding events (like touch and motion) in iOS.
UIResponder, UIEvent and UIControl
In order to handle system events, UIResponder subclasses can register themselves as capable of handling specific UIEvent types by overriding the methods specific to that type:
Although UIResponders can fully detect touch events, handling them isn’t an easy task. How do you differ between different types of touches?
Internally, touching this button results in the following:
If you run this, you’ll see that even though the action was sent from a plain UIView with no target, MyViewController’s myCustomMethod will be triggered!
When no target is specified, UIKit will search for an UIResponder capable of handling this action just like in the plain UIEvent example. In this case, being able to handle an action relates to the following UIResponder method:
By default, this method simply checks if the responder implements the actual method. «Implementing» the method can be done in three ways, depending on how much info you want (this applies to any native action/target component in iOS!):
Now, what if the responder doesn’t implement the method? In this case, UIKit uses the following UIResponder method to determine how to proceed:
By default, this will return another UIResponder that may or may not be able to handle the desired action. This repeats until the action is handled or the app runs out of choices. But how does the responders know who to route actions to?
The Responder Chain
Responder Chain Custom Uses
Although the Responder Chain is fully handled by UIKit, you can use it in your favor to solve communication/delegation issues.
This works pretty much like regular notifications, with the difference being that while notifications will trigger everyone that registers them, this efficiently iterates the Responder Chain and stops as soon as the first BlinkableView is found.
As mentioned before, even architectures can be built out of this. Here’s the skeleton of a Coordinator structure that defines a custom type of event and injects itself into the Responder Chain:
The way this works is that each CoordenableViewController holds a reference to its original next responder (the window), but overrides next to point to the Coordinator instead, which in turn points the window at its next responder.
This allows the Coordinator to receive system events, and by defining a new PushScreenEvent that contains info about a new view controller, we can dispatch a pushNewScreen action that is handled by these Coordinators to push new screens.
With this structure, UIApplication.shared.push(vc: newVC) can be called from anywhere in the app without needing a single delegate or singleton as UIKit will make sure that only the current Coordinator is notified of this action, thanks to the Responder Chain.
The examples shown here were highly theoretical, but I hope this helped you understand the purpose and uses of the Responder Chain.
References and Good reads
Thanks for reading! If you want to see more Swift / iOS content like this, follow me on Twitter!
Articles about advanced Swift development in excruciating detail, for free!
Русские Блоги
[Разработка IOS] Responder Chain для доставки событий
СмотретьБлог Casa TaloyumУвидеть очень красивый способ доставки событий.
Несколько способов доставки мероприятия:
Существуют различные ячейки, такие как сведения о продукте, и в ячейке есть различные события кнопок. В ячейке может быть несколько уровней пользовательского интерфейса. Как передать события многих очень сложных страниц пользовательского интерфейса этого уровня в Контроллер для обработки. Общий метод заключается в использовании блока для передачи слоя за слоем или использования делегата для передачи слоя за слоем. Когда уровней много, это очень хлопотно.
Введение в доставку событий iOS
Цепочка ответов на события системы iOS передается на уровне пользовательского интерфейса. Если наши собственные события могут передаваться в цепочке ответов системы, то мы можем выпрыгнуть из проблем уровня UI.
Позвольте мне сначала поговорить о доставке событий системы iOS. Она передается сверху вниз через UIResponder. UIWindow, UIViewController, UIView и т. Д. Все унаследованы от UIResponder, поэтому все они могут реагировать на события, например:
Вообще говоря, независимо от того, на каком уровне находится представление на странице, в его цепочке иерархии есть уровень UIViewController. Таким образом, мы можем отметить событие и обработать его на уровне UIViewController.
Метод реализации
Добавить категорию в UIResponder
При нажатии кнопки или возникновении события вызовите
Перепишите метод routerEventWithName в представлении, которое нужно обработать для обработки события, которое обычно может находиться в UIViewController.
С тем, что я написал ранееСхема развязки TableViewCell с протоколом, Вы можете полностью поддерживать возможность повторного использования ячеек, и в то же время рефакторинг или другие изменения в Контроллере будут небольшими.
Github ResponderChainDemoПоставьте звезду, если считаете, что это хорошо.
Получить текущий первый респондент без использования частного API
Я отправил свое приложение чуть более недели назад и получил страшное письмо с отказом сегодня. Это говорит мне, что мое приложение не может быть принято, потому что я использую непубличный API; в частности, это говорит,
Непубличный API, включенный в ваше приложение, это firstResponder.
Теперь вызов API-интерфейса на самом деле является решением, которое я нашел здесь на SO:
Как вывести текущего первого респондента на экран? Я ищу способ, которым мое приложение не будет отклонено.
В одном из моих приложений я часто хочу, чтобы первый респондент подал в отставку, если пользователь нажимает на фон. Для этого я написал в UIView категорию, которую я вызываю в UIWindow.
Следующее основано на этом и должно вернуть первого респондента.
iOS 7+
Swift:
Пример использования в Swift:
Если вашей конечной целью является просто подать в отставку первого респондента, это должно работать: [self.view endEditing:YES]
Распространенным способом манипулирования первым респондентом является использование целевых действий с нулевым значением. Это способ отправки произвольного сообщения в цепочку респондента (начиная с первого респондента) и продолжения вниз по цепочке, пока кто-то не ответит на сообщение (не реализовал метод, соответствующий селектору).
В случае отклонения клавиатуры это наиболее эффективный способ, который будет работать независимо от того, какое окно или представление является первым респондентом:
(Спасибо BigZaphod за напоминание о концепции)
UIResponder + FirstResponder.h
UIResponder + FirstResponder.m
Хитрость в том, что отправка действия на nil отправляет его первому респонденту.
Вот расширение, реализованное в Swift на основе самого превосходного ответа Якоба Эггера:
Swift 4
Это не красиво, но то, как я оставляю firstResponder, когда я не знаю, что это за ответчик:
Создайте UITextField, либо в IB, либо программно. Сделать это скрытым Свяжите это с вашим кодом, если вы сделали это в IB.
Затем, когда вы хотите отклонить клавиатуру, вы переключаете респондент в невидимое текстовое поле и немедленно отмените его:
Для Swift 3 и 4 версии ответа Невина :
Вот решение, которое сообщает о правильном первом респонденте (например, многие другие решения не будут сообщать UIViewController о первом респонденте), не требует зацикливания в иерархии представления и не использует частные API.
Он использует метод Apple sendAction: to: from: forEvent:, который уже знает, как получить доступ к первому респонденту.
Нам просто нужно настроить его двумя способами:
Первым респондентом может быть любой экземпляр класса UIResponder, поэтому существуют другие классы, которые могут быть первым респондентом, несмотря на UIViews. Например, UIViewController также может быть первым респондентом.
В этом разделе вы найдете рекурсивный способ получения первого респондента, просматривая иерархию контроллеров, начиная с rootViewController окон приложения.
Вы можете получить первого респондента, выполнив
Однако, если первый респондент не является подклассом UIView или UIViewController, этот подход потерпит неудачу.
Используя Swift и с конкретным UIView объектом, это может помочь:
Просто поместите его в свой UIViewController и используйте его так:
Обратите внимание, что результатом является необязательное значение, поэтому оно будет равно нулю, если firstResponser не был найден в данных подпредставлениях представлений.
вы можете добавить распознаватель жестов касания в файл xib или раскадровки и подключить его к действию,
выглядит примерно так потом закончил
В данном случае Swift-версия удивительного подхода Якоба Эггера:
Это то, что я сделал, чтобы узнать, что UITextField является firstResponder, когда пользователь нажимает Сохранить / Отмена в ModalViewController:
Это то, что у меня есть в моей категории UIViewController. Полезно для многих вещей, в том числе получения первого респондента. Блоки отличные!
При включенной категории UIResponder можно по закону попросить UIApplication объект сообщить вам, кто является первым респондентом.
Вы можете выбрать следующее UIView расширение, чтобы получить его (кредит от Daniel) :
Вы также можете попробовать вот так:
Я не пробовал, но это кажется хорошим решением
Я рекомендовал попробовать это:
Это хороший кандидат на рекурсию! Нет необходимости добавлять категорию в UIView.
Использование (с вашей точки зрения контроллера):
Вы можете назвать приватный API, как это, яблоко игнорировать:
Быстрая версия ответа @ thomas-müller
Я хотел бы поделиться с вами моей реализацией для поиска первого респондента в любом месте UIView. Я надеюсь, что это помогает и извините за мой английский. Спасибо
Большинство ответов здесь не могут найти текущего первого респондента, если он не находится в иерархии представлений. Например, AppDelegate или UIViewController подклассы.
Сначала давайте реализуем его обратную версию, используя next свойство UIResponder :
Однако нам все еще нужно разрешение сверху вниз, единственное, var чтобы получить текущий первый респондент.
Сначала с иерархией представления:
И, наконец, мы можем построить это:
Поэтому, когда вы хотите получить первого респондента, вы можете позвонить:
Open URLs using the iOS UIResponder chain in Swift
When creating iOS apps we find the need to have some buttons or UIControls opening a URL link.
This can be easily done by calling the open(_:options:completionHandler:) method of UIApplication. With the addition of the SFSafariViewController in iOS 9, this opened new possibilities to show links without the user leaving the app.
While the UIApplication open(_:options:completionHandler:) can be called from anywhere in your code, presenting a SFSafariViewController can happen only from a UIViewController.
A real-world use case that leads to this solution
I was recently working on an application that parsed and builds interactive documents from a JSON object. Among the multiple objects that constitute an interactive document, there is a simple link object. If the user taps the link, we present the content in a SFSafariViewController ( SafariVC) without leaving the app.
The app hierarchy looked similar to the following diagram:
At the root we have a navigation controller and several embeds after we can see the link. The tricky part was conveying the user interaction from the link all the way up to the root view controller so we can present the SafariVC by pushing it in the Navigation stack.
A multitude of solutions can be used to solve this issue. One of them is to assign the DocumentController as a Target for the TouchUpInside event on the Link, and expose a Delegate property with a didTapLink(_ link: URL) callback. Then ask the DocumentGridController to assign its parent as the delegate for its children.
This seems like a viable solution but it involves a lot of constraints:
— the LinkView needs to be a UIControl,
— the DocumentController needs to know about the Link,
— the TopicViewController needs to adhere to a Protocol,
— the DocumentGridController needs to know its parent and assign it to its children. This becomes very messy and hard to maintain.
On top of that, what would happen if we presented a DocumentController from elsewhere? We should always remember to assign a delegate and implement the corresponding protocol.
The solution: Propagating links via the Responder Chain
What if we can use the same logic as the touch handling in iOS uses?
The goal is to propagate the action of opening a link up in the hierarchy. So each object in that hierarchy can decide if it can open the URL or should pass it to the next one.
If you are not familiar with the UIResponder chain, I would recommend you reading: Using Responders and the Responder Chain to Handle Events from Apple documentation.
Here is a diagram showing how the propagation will take place if the UIResponder chain is used to carry the URL call.
Here is a snippet of the Protocol and Implementation:
Every class that conforms to URLHandler can receive URL requests via the responder chain. The function propagateURL in the UIResponder extension will trigger the propagation:
So since our LinkView is a UIResponder, it can call propagateURL to start the propagation. The propagation will continue until it reaches the TopicViewController that conforms to URLHandler protocol. All the intermediate views and controllers will forward the calls since they do not conform to the URLHandler protocol.
Inside our HandleURL method in the TopicViewController we can instantiate a SafariVC and present it.
Handling specific URLs
By having the URLHandler conformance an object can decide whether it can or can’t handle a URL. For example, our TopicViewController can check the URL domain and decide whether to open it in SafariVC or continue its propagation. In our app, we chose to open all links using the SFSafariController except Youtube links.
Asynchronous execution
Unlike the touch events propagation, the URLHandling can happen asynchronously. The propagation only continues when the completion block of the URLHandler method is called. This allows checking a remote server for Whitelisted URLs for example.
Bonus
Since UIApplication is also a UIResponder and is always at the end of the responder chain, we can use that to channel all unhandled URL propagation into the Safari app.
Conclusion
Using the UIResponder chain to propagate URLs can help decoupling classes. It works on the fact that if an object cannot handle a URL, then it should pass it to the next object and so forth. Using the UIResponder chain ensure a logical hierarchy that ends with the application delegate.
Responder Chain you should understand in iOS
Apps receive and handle events using responder objects. A responder object is an instance of the UIResponder class, and common subclasses include UIView, UIViewController, and UIApplication. UIKit manages most responder-related behavior automatically, including how events are delivered from one responder to the next.
A UIResponder is a potential recipient of touches. A UIView is a UIResponder. and is thus the visible recipient of touches. There are other UIResponder subclasses, but none of them is visible on the screen. The users see a view via virtue of its underlying layer and the user touches a view via virtue of the fact that it’s a responder.
If the first responder [to an event or action message] cannot handle an event or action message, it forwards it to the “next responder” in a linked series called the responder chain. The responder chain allows responder objects to transfer responsibility for handling an event or action message to other objects in the application.
If an object in the responder chain cannot handle the event or action, it resends the message to the next responder in the chain. The message travels up the chain, toward higher-level objects, until it is handled. If it isn’t handled, the application discards it.
There is no such thing as a second responder.
The default chain is only the default tho, so after you have managed the First Responder it is down to you to insert, remove and append items to its responder chain to achieve your desired aim.
The responder chain is quite important and complicated, you should take time to read the Apple docs about it.
UIbutton/UITextField — (nextResponder)-> UIView — (nextResponder)-> UIViewController
— (nextResponder)-> UIWindow — (nextResponder)-> UIApplication — (nextResponder)-> UIApplicationDelegate
The path of an event. The general path of an event up the responder chain starts with a view — the first responder or the view under the mouse pointer or finger. From there, it proceeds up the view hierarchy to the window object and then to the global app object. However, the responder chain for events in iOS adds a variation to this path: If a view is managed by a view controller and if the view cannot handle an event, the view controller becomes the next responder.
If you want to know how responder works, you can print it as you think: