А в ваших iOS приложениях IBOutlet уже private?
Вы наверняка использовали Storyboard или XIB для верстки интерфейсов? Верстать из кода это прекрасно, но иногда намного проще понять как устроен какой-то из компонентов интерфейса, увидев его, а не прочитав. В этой записи я хочу обсудить необходимость использования для IBOutlet модификатора private.
Разработчиков, для которых инкапсуляция IBOutlet является очевидной, тут вряд ли что-то удивит, зато может быть интересен опрос в конце статьи.
Представим, что вы собираетесь создать IBOutlet (ссылку на View с Storyboard) для какого-нибудь из ваших UILabel. При перетаскивании мышкой Xcode заботливо создаст нам что-то вроде
Я долгое время считал эту конструкцию оптимальной, до того момента как мой коллега не спросил — а почему твои IBOutlet не private?
В самом деле, зачем мне оставлять все IBOutlet-ы доступными извне?
Представим себе классическую задачу — у нас есть ячейка, в которой отображается, к примеру, чей-то контакт
С помощью добавления private к привычным нам IBOutlet можно гарантировать, что указанные поля ячейки не будет заданы из другого класса. Особенно это может быть полезно при командной работе, когда кто-то по неосторожности / нехватке времени / глупости (нужное подчеркнуть) попробует задать цвета, текст или какие-то другие свойства у Label-ов ячейки прямо в методе tableView(_:cellForRowAt:).
А представьте, что ячейка или целый ViewController содержит множество IBOutlet-ов, что настроек отображения масса. Не проще ли обезопасить себя добавлением private, чем потом искать почему внешний вид элемента вдруг изменился или откуда-то появился Gesture Recognizer, который задает неожиданное поведение?
P.S.: Если после прочтения вам захочется использовать private для IBOutlet-ов, то для простоты можно завести для этого снипет в Xcode.
Ниже приведен опрос, если вы захотите прокомментировать свой вариант ответа, welcome в комментарии.
Правильная передача данных


Правильная передача данных
Доброго времени суток, друзья!
Сегодня мы рассмотрим передачу данных между ViewControllers. На самом деле передача данных между ViewControllers может показаться тривиальной задачей, но если учесть, что любое реальное приложение для iOS будет иметь много ViewControllers, то коммуникация становится их важной частью. Неправильное понимание этого может привести к трудному исправлению ошибок.
В целом существует много способов сделать это, но лишь некоторые из них являются лучшей практикой.
В этой статье я покажу вам такие практики передачи данных:
Передача данных вперед
Передача данных происходит каждый раз, когда на экране появляется новый ViewController.
Это может произойти через segue или программно.
Передача данных вперед между ViewController с использованием segues
Заметка
Всегда правильно задавайте идентификатор для segue. Хорошей практикой будет называть идентификатор тем, что он делает. Потому что может быть ситуации когда Вам нужно передать 2 и более segue в один контроллер. В дальнейшем это поможет свободно ориентироваться в проекте.
Передача данных вперед между ViewController без segues
Иногда вы можете подключить ViewController программно, а не использовать segue.
И готово! Так как в SecondViewController не поменялся код, показываем код FirstViewController :
Передача данных в обратном направлении
Передача данных назад в приложении iOS так же важна, как и их перемещение вперед. Пользователи часто возвращаются к предыдущему экрану, который они посетили.
Когда пользователь взаимодействует с вашим приложением, вы должны обновлять эти предыдущие экраны. Это не происходит автоматически, поэтому вы можете использовать разные методы.
Передача данных в обратном направлении через unwind segue
Передача данных в обратном направлении при помощи делегата
Иногда техники, которые Вы видели, все еще недостаточны.
Заметка
Для закрепления можете сами попробовать сделать это, взяв примеры из 1.2 Передача данных вперед между ViewController без segues.
Продвинутые техники
Замена делегирования на замыкания (closures) Swift
Некоторые разработчики используют замыкания ( closures ) Swift для передачи данных назад между ViewControllers. Этот метод похож на делегирование, но более гибкий. Это также причина, почему я обычно рекомендую не использовать его.
И теперь в FirstViewController также устанавливает связь с этим замыканием, когда происходит переход. Но в этом случае вместо передачи ссылки на себя, он передает замыкание.
Также обратите внимание, что замыкание содержит ссылку на себя. Таким образом, как и делегирование, использование замыканий по-прежнему создает связь между двумя ViewController.
Этот подход немного более лаконичен, чем делегирование. Но использование замыканий также имеет ряд особенностей / недостатков, которых нет у делегирования.
Если вам нужно более одного замыкания для связи с предыдущим контроллером представления, вам нужно сохранить свойство для каждого из них. При делегировании весь интерфейс выделяется внутри протокола, и вам нужно только одно свойство делегата.
На мой взгляд, замыкания лучше работают как обратные вызовы для асинхронных задач, таких как сетевые запросы или анимации. Делегирование является лучшим решением для связи ViewController.
Неправильные техники
Мы познакомились с одними из лучших практик для передачи данных между ViewController. Но к сожалению, в просторах интернета наблюдается и много неправильных.
В этом разделе мы рассмотрим, какие из них, и почему вы не должны их использовать.
Не используйте UserDefaults iOS
В iOS UserDefaults хранят пользовательские настройки, которые должны сохраняться между запусками приложения.
Кроме того, вы можете хранить только простые типы данных в UserDefaults в форме списков свойств. Это означает, что вам нужно преобразовать любой пользовательский тип, прежде чем вы сможете поместить его туда.
В общем, ваше приложение должно получать доступ к UserDefaults через одну точку, которая обычно является настраиваемым контроллером совместно используемой модели.
Не используйте Notifications
Notifications в iOS дают вам канал, по которому какой-то код может отправлять сообщение другим объектам, на которые он не имеет прямой ссылки.
Я видел, как многие разработчики используют Notifications для передачи данных между контроллерами представления. Это не то, для чего они нужны!
Выводы
Как я и говорил, существует много способов передачи данных между ViewController. Но только некоторые из них можно назвать хорошей практикой. Поначалу другие могут показаться удобными, но потом они могут создать Вам проблемы в будущем.
Поэтому выбирайте правильный подход и двигайтесь дальше!
Swift put multiple IBOutlets in an Array
I made these (marked with red border) IBOutlets using ctrl + drag
But i don’t like to have the exact same line 9 times (DRY)
How do i put these IBOutlets in an Array?
5 Answers 5
you can define a generic outlet collection in Swift like this:
or for e.g. UIButton objects:
you can find your collections under the Outlet Collections group as usually are in the File’s Owner:
it would look on my console after connecting 5 random buttons:
Follow these steps to create an array of outlets an connect it with IB Elements:
@IBOutlet var objectCollection: [Object]
This is for macOS (should be similar for iOS) and I do not find an «Outlet Collections» in my storyboard (looks like they took that option out). So I put all my buttons in an NSStackView and linked the stack from storyboard
and then I looped over them to make changes accordingly
you can also use tag instead of index
Start with the two view pane where you see both your code and the storyboard. When you make your first IBOutlet connection from the UI to your code, just look carefully at the Connection drop down field and select the option called «Outlet Collection». This will automatically create an array of IBOutlets. Next just look for the little black circle within a circle that is placed in your code where the array is created. Just drag from this circle to all the other UI objects you want to connect to that same collection (not sure if you can mix types). Similarly you can connect all the objects to one Action by dragging from the first black dot created to all the other objects you want to wire up to that action. Also consider EnumerateSequence() to help in working with this Collection. Sweet right?
Передача данных между контроллерами в Swift
О Свифт
Swift — это замечательный язык программирования, который Apple представила во время WWDC 2014, и теперь почти все разработчики Apple используют его для программирования приложений iOS и OS X. Он очень гибкий, более простой в использовании, чем его предшественник Objective C, и сэкономит вам драгоценные минуты при кодировании с помощью XCode.
Это мощный и интуитивно понятный язык программирования для iOS, OS X, tvOS и watchOS. Написание кода Swift интерактивно и увлекательно, синтаксис лаконичен, но выразителен, а Swift включает в себя современные функции, которые любят разработчики. Если вы еще не используете Swift, ознакомьтесь с некоторыми другими нашими великолепными учебниками по языку Swift:
Об этом уроке
Настройка проекта XCode
Прежде всего, создайте новый проект XCode. Выберите Single View Application и назовите проект так, как вам нравится. Вы найдете файл ViewController.swift в списке файлов на левой панели и интерфейс контроллера в раскадровке.
Начните с перетаскивания нового View Controller из библиотеки объектов на раскадровку.
Теперь добавьте UILabel и UIButton к контроллеру. Вы можете найти их в библиотеке объектов и перетащить в ваш первый контроллер. Затем дважды щелкните UILabel и введите текст, который вы хотите передать второму контроллеру. Я только что установил его текст « Текст для передачи ».
На втором контроллере перетащите новую UILabel куда хотите и просто оставьте текст таким, какой он есть.

Разбейте окно XCode на две части, нажав кнопку « Помощник редактора» в правом верхнем углу. Теперь у вас будет раскадровка слева, а соответствующий файл .swift — справа.


Как только вы закончите соединять все представления, разделите окно XCode на две части и выберите первый контроллер на раскадровке. Теперь с правой стороны отобразится файл ViewController.swift, и вы будете готовы написать в нем некоторый код.
Давайте код!
Биндинги в Swift. Делаем первые шаги к MVVM
Доброго времени суток. Эта статья будет полезна тем, кто устал изо дня в день бороться с изменяемостью данных в интерфейсе, тем, кто еще не знает о существовании MVVM, и тем, кто сомневается, что данный паттерн можно успешно применять на практике при разработке iOS приложений. Заинтересовавшихся прошу под кат.
Не могу назвать себя опытным iOS разработчиком. Хотя знакомство с миром iOS состоялось несколько лет назад, стремление прокачаться в разработке приложений под iOS появилось у меня совсем недавно. Путь мой был тернист. Obj-C сразу не впечатлил, хотелось разрабатывать приложения на чем-то знакомом. Поэтому в ход шли PhoneGap, Apcelerator Titanium и вот это все. Но, естественно из этих начинаний ничего не вышло. После длительного перерыва компания, в которой я работаю, всерьез задумалась над разработкой мобильного приложения. Не стал ничего выдумывать и упрощать себе жизнь — выполнил работу исключительно на ObjC без использования сторонних фреймворков. И это была боль для меня. Простые вещи оказались сложными, я не мог управиться с autolayout, на код было невозможно смотреть. Поэтому в следующем проекте в ход был пущен Xamarin Forms. Поработав порядка двух месяцев над проектом, стало ясно, что данная технология еще далека от совершенства (в итоге оказалось, что проект находился в beta статусе, но об этом мало где упоминалось). Но за время работы с Xamarin Forms я проникся многими паттернами, которыми был пропитан этот проект, более того мне пришлось сделать кучу кастомных компонентов, что привело к более ясному пониманию работы UIKit. В тот момент, когда стало ясно, что наш проект должен быть переписан на натив, Swift стремительно приближался к релизу. Я прочел книгу по этому языку и чувствовал в себе силы, чтобы начать все сначала. Но первый опыт все еще напоминал о себе, поэтому я стал копать в сторону MVVM в iOS. Уж больно понравилась мне эта концепция.
На тот момент все статьи, что попадались мне на глаза, предлагали решать проблему с помощью ReactiveCocoa. Взглянув на примеры кода этой библиотеки, я понял, что мне еще учиться и учиться, т.к. я ничего не понимал в том, что видел. Для Swift также предлагали использовать ReactiveCocoa. Собственно статья от Colin Eberhardt стала для меня отправной точкой. Но вскоре мне пришлось столкнуться с тем, что подход, описанный вышеупомянутым автором, приводил к утечкам памяти. Видимо я что-то делал не так и тогда не понимал что именно. Плюс ReactiveCocoa оставался для меня черной коробкой. Было решено избавиться от этой библиотеки, учитывая, что использовалась она лишь для связывания view моделей с view. Наткнулся на проект Observable Swift, который решал проблему связывания. Вскоре наш проект был завершен, а на горизонте новый, и мне хотелось к нему основательно подготовиться.
Постановка задачи
На данный момент я не могу представить себе, как можно безболезненно привнести MVVM в UIKit. Имеется ввиду тот самый MVVM, который я увидел в Xamarin Forms и который меня так впечатлил. Скорее всего для этого придется написать фрэймворк поверх UIKit и привязать разработчика к этому фрэймворку. Мы же пойдем по пути наименьшего сопротивления: будем использовать то, что нам дает Apple. Но при этом будем стремиться к более декларативному описанию UI.
Dynamic
Начнем с малого и, вместе с тем, самого главного. Нам необходимо иметь возможность узнавать об изменениях состояния какой-либо переменной и как-то реагировать на эти изменения. Напомню, что мы стремимся к простоте и лаконичности. И в этом случае Swift предстает во всей красе. Он нам дает дженерики, лямбды с потрясающим синтаксисом, observable properties. Так давайте сваяем из этого нечто.
Теперь у нас появилась возможность следить за изменением значения value. На практике это будет выглядеть примерно так:
Добавим поддержку слушателей для нашей изменяемой сущности. Слушателем будет являться анонимная функция, в аргумент которой мы будем передавать новое значение value.
Метод addListener просто добавляет хэндлер в свой список слушателей, а метод bind делает тоже самое, но при этом сразу вызывает добавленного слушателя и передает ему текущее значение value.
Благодаря использованию дженериков нам не нужно проверять или делать приведение типов данных. Компилятор сделает это за нас. Например в следующем случае код не будет скомпилирован:
Компилятор знает, что аргумент нашего слушателя типа Int и мы не можем присвоить значение этого аргумента полю text объекту класса UILabel, так как тип этого поля String. Более того, благодаря упрощенному синтаксису анонимных функций мы получили возможность добавлять слушателей без лишней писанины. Но нет предела совершенству. Мы же можем определить пару-тройку операторов, либо перегрузить имеющиеся с целью еще большего сокращения кода.
На практике описанные выше примеры приведут к утечкам памяти. Вот пример:
Очевидно, что теперь функция-слушатель и self жестко связаны друг с другом и объект класса MyViewController никогда не удалиться. Чтобы этого не случилось, необходимо ослабить связь:
Так лучше. Но есть одно но. Нет гарантии, что функция-слушатель не будет вызвана после удаления объекта MyViewController. Чтобы обезопасить себя, мы используем weak:
Поэтому от идеи поддержки двух типов слушателей пришлось отказаться и воспользоваться подобными ухищрениями:
Конечно, можно было бы отказаться от использования weak в пользу передачи динамическому объекту помимо функции-обработчика еще и ссылки на объект и не вызывать функцию, если объект вдруг оказался удаленным. Именно такой подход и используется в библиотеке Bond. Но это был не мой путь 🙂
Упрощение работы с UIKit
Согласитесь, неприятно постоянно описывать одни и те же лямбды для связывания текста и UILabel. Хочется простоты:
Нет ничего невозможного. Ведь мы можем без особого труда прийти и к такому синтаксису. Идея реализации опять же любезно позаимствована у Bond. Идея проста: будем хранить у объекта какого-либо вида поле, у которого есть слушатель, и мы сможем привязать этого слушателя к динамическому объекту.
Объект класса PropertyModifier будет создаваться самим видом, а в конструктор будет передаваться лямбда с кодом, который изменяет значение у определенного поля вида.
Замечу, что в extension мы не можем описывать хранимые поля (stored properties), поэтому на помощь приходит ObjC Runtime и функции objc_setAssociatedObject, objc_getAssociatedObject. Теперь мы можем делать так:
Куда лучше. Но это еще не все. Мы можем выделить какое-либо наиболее используемое свойство вида и назначить ему PropertyModifier по-умолчанию.
Вот и все. UILabel обзавелся стандартным PropertyModifier, который изменяет значения поля text. И мы пришли к назначенной цели, а именно можем создать связь следующим образом:
Команды
Одна из примечательных концепций в Xamarin Forms, которая пришлась мне по душе — это команды. На деле мы можем описать команду с помощью двух функций: одна возвращает true или false, указывая на то, что команда может быть выполнена, а вторая — действие, которое выполняет команда. Допустим у нас есть кнопка (UIButton). У кнопки есть поле enabled, кнопка может быть нажата пользователем, после чего должно произойти какое-то действие. Помните, что мы стремимся к декларативности описания поведения интерфейса? Так давайте распространим эту идею на наши контролы.
Итак, у нас появилась команда, у которой есть поле enabled и функция, которая должна быть выполнена при вызове метода execute. Мы должны связать нашу команду с кнопкой. Для этого завели протокол Commander с методом setCommand. Реализуем наш протокол для UIButton, связав динамическое поле команды enabled с соответствующим свойством UIButton. Так же мы перегрузили оператор >> для удобства. Что получаем в итоге:



















