что такое uiwindow swift

Русские Блоги

UIView и UIWindow на основе Swift

Во-первых, связь между UIView и UIWindow

Как видите, UIView представляет собой прямоугольную область на экране и занимает очень важное место в приложении, поскольку почти все визуальные элементы управления в iOS являются подклассами UIView.
UIView наследуется от UIResponder, это холст, который отвечает за отображение, если окно сравнивается с рамкой изображения. Мы постоянно снимаем, заменяем или накладываем холст на раму, или накладываем другие холсты на холст, размер, конечно, определяет художник. С холстом мы можем делать с ним все, что захотим. Я выложу много простых вещей в библиотеке. Если вы разместите слишком много вещей, это не пойдет на пользу. Друзья, перейдите в файл библиотеки. Этот класс находится в UIView.h.

Функции UIView:

Основные функции окна:

Во-вторых, создание и свойства UIView

1. Создание UIView

Определите глобальный объект UIView

2. Метод свойств UIView

UIView может содержать много Subviews (других UIViews), и эти Subviews имеют так называемые иерархические отношения друг с другом, что немного похоже на концепцию слоев в программном обеспечении для рисования. Следующий код демонстрирует несколько уровней управления (Subview) на часто используемые методы.

Переместите Subview вперед или назад на один слой в UIView.Продвижение вперед закроет Subview нижнего слоя, а перемещение назад будет перекрыто Subview верхнего уровня.

Используйте индекс Index в UIView для обмена уровнями слоев двух подпредставлений.

Используйте имя переменной Subview, чтобы получить значение индекса (Index) в UIView.
// Получить индекс
let index = self.view.subviews.indexOf (имя вложенного представления)

Получите все Subviews в UIView, вызов этого метода вернет NSArray и перечислит эти Subviews в порядке от начала к началу.
UIView().subviews

3. На что следует обратить внимание в UIView

В-третьих, создание и свойства UIWindow

1. Создание UIWindow

Определите глобальную переменную UIWindow

2. Введение в свойства UIWindow

Сделать главное окно используемого объекта отображаемым на переднем плане экрана. Примечание:! Означает, что окно == nil запускается, но программа window == nil дает сбой!

Установите цвет фона окна

Вот лишь краткое введение в UIView и UIWindow, в основном для тех, у кого есть определенная основа OC. Я продолжу знакомить с ними в следующих статьях. Надеюсь, вы обратите внимание.

Источник

UIWindow

Доступный в iOS 2.0 и позже.

UIWindow класс определяет объект, известный как окно, управляющее и координирующее представления отображения приложения на экране устройства. Если приложение не может вывести на экран содержание на экране внешнего устройства, приложение имеет только одно окно.

Две основных функции окна должны обеспечить область для отображения ее представлений и распределять события представлениям. Для изменения содержания отображения приложения можно изменить корневое представление окна; Вы не создаете новое окно. Окно принадлежит уровню — обычно, UIWindowLevelNormal — это представляет, где это находится на оси z относительно других окон. Например, системное окно предупреждения появляется выше нормальных окон приложения.

При использовании раскадровок и шаблонов приложений XCode для создания приложения, окно создается для Вас. Если Вы принимаете решение создать окно в Интерфейсном Разработчике, несомненно, выберут опцию Full Screen at Launch в инспекторе Атрибутов так, чтобы окно было измерено соответственно для текущего устройства. Поскольку окно не получает сенсорные события за пределами своих границ, и представления не отсекаются к границам окна по умолчанию, неправильно размерное окно не могло бы быть в состоянии поставить сенсорные события всем своим представлениям.

Для получения дополнительной информации о том, как использовать окна, см. Многократное Руководство по программированию Дисплея для iOS.

Источник

Обработка жестов в iOS

В разработке приложений для iOS важно знать как работает система изнутри. По обработке событий много разной интересной инфы, но хотелось бы структурировать и собрать все в одну. Что я и попытаюсь сделать.

Структура:

Разбор основных кейсов

UIKit

Начнем с главного. Что такое ваш UIKit? Если посмотреть в официальную доку, то мы увидим такую инфу:

The UIKit framework provides the required infrastructure for your iOS or tvOS apps. It provides the window and view architecture for implementing your interface, the event handling infrastructure for delivering Multi-Touch and other types of input to your app, and the main run loop needed to manage interactions among the user, the system, and your app.

Так. Здесь мы узнали, что этот фреймворк помогает нам создавать архитектуру окон и вьюшек, инфраструктуру обработки событий, а также основной цикл выполнения. Давайте пройдемся по порядку.

Наше приложение стартует с имплементации экземпляра класса для UIApplication. Каждое iOS приложение имеет ровно один экземпляр UIApplication. Он маршрутизирует события пользователя, а также с помощью UIApplicationDelegate информирует о важных событиях (запуске приложения, не хватки памяти, завершении работы приложения).

Давайте посмотрим как это выглядит в коде. Если бы мы юзали сториборды, то это происходило автоматически. UIApplicationMain проверяет, использует ли ваше приложение сториборды. Он знает, используете ли вы main storyboard и каково ее имя, просмотрев ключ Info.plist “Main storyboard file base name” (UIMainStoryboard- File)

Но мы попробуем все сделать кодом:

UIApplicationMain создает экземпляр UIApplication и сохраняет этот экземпляр. К которому позже можно обращаться через UIApplication.shared

Затем он создает экземпляр класса делегата приложения. Система знает что это за класс, потому что мы пометили его @main (в ранних версиях @UIApplicationMain)

UIApplicationMain вызывает у делегата приложения метод application(_:didFinish- LaunchingWithOptions:).

Но интерфейс приложения не отображается, пока содержащее его window не станет ключевым окном приложения. Функция makeKeyAndVisible поможет нам

Читайте также:  что делать при пульсе 135

Но начиная с iOS 13, функции AppDelegate были разделены между AppDelegate и SceneDelegate. Это результат новой функции поддержки многооконного режима, представленной в ОС iPad, которая разделяет работу AppDelegate на две части.

UIApplicationMain вызывает у делегата приложения метод application(_:didFinish- LaunchingWithOptions:).

UIApplicationMain создает UISceneSession, UIWindowScene и экземпляр, который будет служить делегатом сцены окна

В Info.plist нужно указать (в виде строки) какой класс будет делегатом

UIApplicationMain проверяет, использует ли ваша начальная сцена сториборды. (В Info.plist нужно указать имя сториборда)

Если сцена имеет сториборд, то UIApplicationMain создает экземпляр UIWindow и назначает его делегатом сцены.

UIApplicationMain вызывает отображение интерфейса путем вызова метода экземпляра UIWindow makeKeyAndVisible.

В делегате сцены вызывается метод scene(_:willConnectTo:options:)

Не ожидайте, что window может быть только одно. Есть такие окна UITextEffectsWindow и UIRemoteKeyboardWindow.

Окей. Мы вроде разобрались как создается главный маршрутизатор всех событий. Но что такое эти события и как они выглядят?

Знакомьтесь. Это UIEvent. Главный объект, который содержит много нужной инфы для обработки событий. Когда обнаруживается системное событие, такое как прикосновение к экрану, UIKit внутри создает экземпляры UIEvent и отправляет их в очередь системных событий (main event loop), вызывая UIApplication.shared.sendEvent().

UITouch

Каждый экземпляр UIEvent содержит одно или несколько объектов UITouch. Для данного объекта UITouch могут произойти только четыре вещи. Они называются фазами касания и описываются свойством var phase: UITouch.Phase:

.began — Палец впервые коснулся экрана; этот экземпляр UITouch только что был создан. Это всегда первая фаза, которая наступает только один раз.

.moved — Палец двигается по экрану.

.stationary — Палец оставался на экране неподвижно. Для чего это нужно? Как только экземпляр UITouch был создан, он должен присутствовать каждый раз, когда прибывает UIEvent для этой последовательности мультитач. Таким образом, если UIEvent прибывает из-за того, что произошло что-то еще (например, новый палец коснулся экрана), UIEvent должен сообщить, что этот палец делал, даже если он ничего не делал

По сути этих 4х фаз достаточно, чтобы описать все действия пальца. Но возможна еще одна фаза:

.cancelled — Система прервала последовательность мультитача, потому что что-то прервало ее. Возможно, пользователь нажал кнопку «Домой» или кнопку блокировки экрана в середине последовательности. Возможно, появилось локальное уведомление.

UITouch также имеет такие свойства:

location(in:), previousLocation(in:) — Текущее и предыдущее местоположение этого касания относительно системы координат view.

timestamp — Когда тач последний раз менялся. Прикосновение получает отметку времени, когда оно создается (.began) и каждый раз, когда оно перемещается (.moved)

tapCount — Если два касания происходят примерно в одном и том же месте в быстрой последовательности, а первое короткое, второе можно охарактеризовать как повторение первого. Это разные сенсорные объекты, но второму будет назначено значение tapCount на единицу больше, чем у предыдущего

view — вьюшка, c которой связано это прикосновение

Когда UITouch впервые появляется (.began), ваше приложение определяет, с каким UIView оно связано. (Позже мы узнаем как это происходит). Затем это же UIView устанавливается как свойство var view сенсорного экрана и остается им. C этого момента этот UITouch всегда связан с этим view (до тех пор, пока этот палец не покинет экран).

Main Event Loop

Когда объект приложения получает событие из очереди событий, он отправляет его в window, в котором произошло пользовательское событие. Window отправляет событие в view, которое является для него наиболее подходящим обработчиком

Сразу после запуска приложение настраивает инфраструктуру для основного цикла событий

Когда приложение запускается, оно также устанавливает основную группу объектов, которые отвечают за отрисовку UI и обработку событий. Эти основные объекты включают window и различные виды вьюшек.

Когда объект приложения получает событие из очереди событий, он отправляет его в window, в котором произошло пользовательское событие. Window отправляет событие в view, которое является для него наиболее подходящим обработчиком

Окей. Вроде все понятно. Мы узнали про главный маршрутизатор событий, узнали о самих событиях. Но как события доходят до точки исполнения?

Responder Chain

Экземпляры UIResponder — основные обработчики событий в приложении. Почти все ключевые объекты являются респондерами (UIApplication, UIWindow, UIViewController, UIView).

Чтобы получать события, респондер должен реализовать соответствующие методы обработки событий и, в некоторых случаях, сообщить приложению, что оно может стать первым респондером

Респондеры получают необработанные данные о событии и должны либо обработать событие, либо переслать его другому объекту-респонденту. По связанному списку от репондера к респондеру.

Если первый респондер не может обработать сообщение о событии или действии, он пересылает его «следующему респондеру». Если объект в цепочке респондента не может обработать событие или действие, он передает сообщение следующему респондеру в цепочке. Сообщение движется вверх по цепочке к объектам более высокого уровня, пока не будет обработано. Если он не обрабатывается, то приложение отбрасывает его.

У респондера есть несколько методов обработки событий:

touchesBegan(_:with:) — во view или window произошло одно или несколько новых касаний.

touchesMoved(_:with:)Сообщает респонденту, когда одно или несколько касаний, связанных с событием, изменились.

touchesEnded(_:with:) — Сообщает респонденту, когда один или несколько пальцев поднимаются из вида или окна.

touchesCancelled(_:with:) — Сообщает респонденту, когда системное событие (например, системное предупреждение) отменяет последовательность касаний.

Аргументы этих методов:

touches: Set — множество прикосновений. Если во множестве только одно касание, то мы получаем его. Если же во множестве много то выполнится first метод (набор неупорядочен, поэтому какой элемент будет первым система выберет произвольно).

Читайте также:  что такое акт сдачи приемки оказанных услуг

event: UIEvent? — сущность объекта UIEvent

Gesture Recognizer

Процесс распознавания жестов довольно сложный механизм. Еще сложней, когда мы хотим обрабатывать разные типы жестов. Решением являются Gesture Recognizers (субкласс UIGestureRecognizer), которые стандартизируют общие жесты и позволяет разделять и инкапсулировать код для разных жестов в разные объекты. Благодаря распознавателям жестов нет необходимости создавать подкласс UIView только для того, чтобы реализовать интерпретацию касания.

Gesture Recognizer — это объект, задача которого обнаруживать, что последовательность мультитач приравнивается к одному конкретному типу жеста. Он прикреплен к UIView. Мы можем добавлять и удалять распознаватели:

UIGestureRecognizer реализует четыре метода касания, но он не является респондером. Поэтому не участвует в responder chain.

По сути это обычный словарь, который хранит все жесты.

В кейсе ниже мы реализуем, используя распознаватель жестов, вьюху, которая позволяет перетаскивать себя в любом направлении одним пальцем.

Window доставляет события касания в словарь распознавания жестов, прежде чем оно доставляет их в hit-testing view

Закрепление доставки событий

Таким образом давайте закрепим доставку тачей от точки прикосновения до точки управления событием:

Когда появляется новое касание, приложение выполняет проверку нажатия, чтобы определить view, к которому прикоснулись. Это view будет навсегда связано с этим касанием и соответственно будет называться hit-test view

Когда происходит другое касание приложение вызывает собственный метод sendEvent(:), который в свою очередь вызывает sendEvent(🙂 окна (window). Window прокладывает путь к прикосновению

Но как этот путь прокладывается?

Hit-Testing

Hit Testing — это рекурсивный поиск среди всей иерархии вьюх к какой прикоснулся пользователь. iOS пытается определить, какой UIView является самой передней вьюшкой под пальцем пользователя, которая должна получить событие касания.

На диаграмме выше hit-testing выполняется каждый раз, когда палец касается экрана. И до того, как какое-либо средство распознавания представления или жеста получит объект UIEvent, представляющий событие, которому принадлежит касание. Полученная UIView становится firstResponder.

Метод hitTest(_: with 🙂 реализует логику проверки касания исключительно для этой вьюхи. Если isUserInteractionEnabled представления имеет значение false, или его isHidden имеет значение true, или его alpha близка к 0,0, то hitTest возвращает nil, что означает, что ни эта вьюха, ни другая из её сабвьюх не могут быть вьюхой для следующего вызова hitTest.

Алгоритм начинается с отправки сообщения экземпляру UIApplication путем вызова sendEvent (_ :). а UIApplication, в свою очередь, передает его UIWindow, вызывая его sendEvent (_ :). Затем UIWindow выполняет сложную логику проверки для каждого тача его иерархии вьюшек

Также нашел схему, которая описывает логику hit testing’а:

Разбор основных кейсов

1. Кейс с выпирающей вьюхой

Кейс очень распространенный. О нем писал яндекс и много ребят на западных ресурсах.

Задача простая. Что будет, если мы нажмем на выпирающую область вьюхи C?

Если вспомнить доку, то ответ будет таким

If a touch location is outside of a view’s bounds, the hitTest(_:with:) method ignores that view and all of its subviews. As a result, when a view’s clipsToBounds property is false, subviews outside of that view’s bounds are not returned even if they happen to contain the touch.

Touch будет проигнорирован вью B. Его координаты не попадают в её область отрисовки. А значит, что самой глубокой вью, которая примет нажатие, будет view A.

В этом случае мы можем переписать вью и написать свой код

Мы определяем, находится ли поинты внутри любых из сабвьюх основной супервьюхи.

Если мы не получаем ни одной, которое включает данную точку, мы проверяем, действительно ли это родительское представление получило обращение

Ниже приведен вспомогательный метод, который проходит через сабвьюхи, и определяет, принадлежат ли поинты какой-либо из супервьюх.

2. Кейс с увеличением нажатия области кнопки

Допустим мы решили увеличить область нажатия кнопки «Х». Пришел дизайнер и сказал, что сложно закрыть эту вьюшку. И нужно, не меняя размера, изменить область нажатия. Тач должен произойти в красной области, а UIButton должен это подхватить

Решение изменить область при тапе с помощью метода pont(inside:):

3. Решение задачи с вырезанной внутри дыркой

Как сделать так, чтобы при нажатии на контент внутри синей вьюшки обрабатывались события вьюшек, под ней лежащих?

Условие задачи — вызвать событие зеленой или желтой вьюшки, которые находятся в центре синей

4. Проход hitTest по слоям

Как мы знаем hitTest работает только с вьюшками. Но это не совсем так.

Допустим у нас есть такой код

Есть 1 вьюшка и в ней 3 слоя.

Но допустим я захочу увеличить один из слоев при таче. Для этого мне стоит переопределить hitTest UIWindow, чтобы ни один из слоев не потерялся

теперь при нажатии hitTest UIWindow проверяет условие наших слоев и выполняет трансформацию

Итого, в этой статье я попытался собрать общую информацию из статей и док, а ткже разобраться в некоторых аспектах сам. Соглашусь, что многие из кейсов специфичные, но все же знать, мне кажется, полезно.

Источник

Understanding UIWindow in iOS

In this article, I ‘ll share what I know about UIWindow

keyWindow

An app can have many UIWindow. The key window is the one that is designated to receive keyboard and other non-touch related events. Only one window at a time may be the key window.

You call makeKeyAndVisible or makeKeyWindow to make a UIWindow become the keyWindow. Note that UIWindow is hidden by default, so makeKeyAndVisible both makes a UIWindow become keyWindow and set its hidden property to NO

Читайте также:  что такое quick freeze в холодильнике

UIWindow is always portrait

Simply add the following code in application:didFinishLaunchingWithOptions:

then rotate the device/simulator, you will see that the green color view is always at position <0, 0>,


The UIWindow’s coordinate system is always in portrait orientation. It applies the rotation by setting its rootViewController’s view’s transform. So starting in iOS 4, application is advised to have rootViewController. See this wonderful answer from rob After rotation UIView coordinates are swapped but UIWindow’s are not?

Keyboard is a window

Note that UIApplication has an instance method «windows»

The app’s visible and hidden windows. (read-only)
This property contains the UIWindow objects currently associated with the app. This list does not include windows created and managed by the system, such as the window used to display the status bar

You can get the keyboard window by traversing this windows array. Code extracted from SVProgressHUD. Note that when the keyboard is shown, the windows array contains another window of type UITextEffectsWindow, that is the keyboard

As said previously, The UIWindow’s coordinate system is always in portrait orientation. Keyboard is a window, so its frame is always (0 0; 320 480) regardless of device orientation. When you rotate the device, the system applies a rotation transform to the keyboard window

User rotates the device to portrait

User rotates the device to landscape

Notification

If you register for UIKeyboardWillShowNotification, you can confirm this

User rotates the device to portrait

User rotates the device to landscape

So in your UIKeyboardWillShowNotification handler, you should convert it to your view coordinate system. Code extracted from Keyboard “WillShow” and “WillHide” vs. Rotation

Statusbar is a window

As said previously, the windows array does not contain the statusbar window. Statusbar window is of type UIStatusBarWindow, you can get it with the following code (extracted from FLEX)

Like the keyboard window, the status bar frame is unchanged. When you rotate the device, the system applies rotation transform to the status bar

User rotates the device to portrait

User rotates the device to landscape

statusBarFrame

UIApplication has a property called statusBarFrame, which is always reported in portrait coordinate system.

User rotates the device to portrait

User rotates the device to landscape
statusBarFrame <<300, 0>, <20, 480>>

If you merely want to get the status height, regardless of orientation, you can just get the max

Notification

When the device is rotated, the system issues UIApplicationWillChangeStatusBarFrameNotification and UIApplicationDidChangeStatusBarFrameNotification.

Let’s now handle UIApplicationDidChangeStatusBarFrameNotification to see

I don’t know why but the WillChange notification gives me the final result, while the DidChange notification give me the old result. By result, I mean the value of the statusBarFrame
I also see that no libraries use the DidChange notification

How to create alert view

To create an alertview, you can simply present an UIViewController (with smaller size). For advanced cases, you must understand the presentation context. And the way to use your alertview is a bit hard. What we always want is a simple call

It ‘s not to say that some view can appears on top of your alert view, so they defeats your purpose as an alertview

The fancy way is to work with UIWindow. I see many libraries use one of these 2 approaches

Don’t use rootViewController approach

The handler is like this, extracted from SVProgressHUD. The idea is manually get the orientation and applies a rotation transform to their view

How to add view to existing window

Libraries have their own strategy to add view to existing window

SVProgressHUD tries to add to the front window

How the OS creates AlertView

Watch WWDC 2014 Session 228 A Look Inside Presentation Controllers, they said

Behind the scenes, the framework creates a window on your app’s behalf, but this predates iOS 8 window rotation behavior, so this window is technically still in portrait.

We then add the action sheet to that window and mimic the transform hierarchy of the presenting view to get into the right orientation.

Use rootViewController approach

Code extracted from FLEXViewExplorerViewController

Since FLEX creates new Window, it must consults the app original Window for rotation capability. Note the infoPlistSupportedInterfaceOrientationsMask and viewControllerForStatusBarAndOrientationProperties, you can learn much from it

UIWindow is a UIView

Since UIWindow is a UIView subclass, you can do many things you already know with UIView. For example, FLEX override pointInside:withEvent: to intercept touch through its toolbar

UIWindow in iOS 8

In iOS 8, Apple introduce Size Classes, and «Rotation is an animated bounds change». And UIWindow is reported in device orientation

WWDC 2014 Session 216 Building Adaptive Apps with UIKit

Trait Environments are a new protocol that are able to return their current Trait Collection, and these include Screens, Windows, View Controllers, and also Views.

All of these are able to return their current Trait Collection to you to use to determine how your interface should be laid out.

UIScreen is now interface oriented:

Источник

Строительный портал