Простой и быстрый PhoneFormatter для UITextField

Приветствую всех! Довольно часто можно встретить форму ввода номера телефона в мобильных приложениях. И самые распространенные примеры — это регистрация и авторизация пользователя. Однако Apple на текущий момент времени не предлагает библиотечного решения для форматирования номера телефона. В данном посте будет продемонстрирован процесс разработки достаточно простого и в то же время быстрого PhoneFormatter.

Разработка PhoneFormatter

Выполнив поиск на данную тему

и проанализировав существующие решения, можно выделить основу, на которой базируется такой Formatter. Это маска, или, как её чаще называют, pattern, в соответствии с которым выполняется форматирование текста.

Самые распространенные способы задания маски — это обозначить символом # любую цифру, а символом * любую букву. Хотя иногда цифры и обозначают символом $, все-таки это менее распространенный подход, поэтому придерживаться стоит первого варианта.

Далее нужен некоторый метод, с помощью которого можно обычную строку с номером телефона, например +12345678910, привести в форматированный вид +1 (234) 567–89–10.

При реализации алгоритма стоит учесть, что строка может включать и отличные от цифр символы, поэтому перед началом форматирования необходимо “очистить” неформатированную строку. Уместней всего для решения этой задачи использовать CharacterSet, поскольку в нем уже определен нужный набор символов и, данный объект реализует протокол SetAlgebra, что позволяет выполнить фильтрацию символов с минимальными вычислительными затратами.

let allowedCharachters = CharacterSet.alphanumericslet filteredInput = String(plainString.unicodeScalars.filter(allowedCharachters.contains))

Алгоритм форматирования на самом деле прост, хотя описание может показаться сложным на первый взгляд.

В цикле выполняется перебор символов неформатированной строки и маски. Каждый символ неформатированной строки проверяется на соответсвие текущему символу маски. Например, если текущий символ маски — это # (число), то если текущий симол неформатированной строки число, то символ сохраняется в форматированную строку и далее будет выполнятся проверка следующего символа маски и следующего символа неформатированной строки. Иначе выполняется проверка текущего символа маски и следующего символа неформатированной строки. Если же текущий символ маски — это не цифра и не буква (например, скобка “(, [, {”), то данный символ сохраняется в форматированную строку, а далее будет выполняться проверка следующего символа маски и текущего символа неформатированной строки.

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

Использование PhoneFormatter в UITextField

Стоит сразу упомянуть о том, что Formatter может быть любым, не только для форматирования номера телефона. Например, для форматирования номера банковской карты, адреса e-mail и т.д. Поэтому стоит хотя бы сформировать фундамент для будущего использования другого рода форматеров. Для этой цели отлично подходит протокол, с необходимым интерфейсом:

Первый метод был рассмотрен выше, а второй предназначен для вызова из метода textField(_:shouldChangeCharactersIn:replacementString) UITextFieldDelegate, поскольку это единственный способ выполнять форматирование сразу (посимвольно) в процессе ввода.

Алгоритм также не должен вызвать затруднений. Сначала определяется строка, введенная пользователем, а потом она обрабатывается форматером. После чего уже форматированная строка отображается пользователю. Однако стоит обратить внимание на строку

return formatted.isEmpty

Дело в том, что при использовании системного autofill (когда клавиатура подсказывает номер телефона), сначала UITextField заполняется пробелом (может быть в зарубежных странах некоторым префиксом), и только после этого, предложенным номером телефона. Поэтому при замене на

return false

системный autofill не будет заполнять UITextField, что конечно же неприемлемо.

В итоге для того, чтобы форматирование срабатывало сразу, по мере ввода номера телефона, осталось реализовать делегат UITextFieldDelegate

Результат ожидаемо потрясающий:

Бонус: SnappyTextField

В последнее время популярными стали UITextField со смещающимися вверх и меняющими свой размер placeholder.

На самом деле тоже довольно простая вещь в реализации.

Следует в качестве placeholder использовать UILabel, поскольку проще всего такую анимацию реализовать с помощью CGAffineTransofrm. Все что понадобится — добавить UILabel в UITextField и выполнять необходимые трансформации этого label. В тоже время данный label отображает текст placeholder, поэтому потребуется изменить реализацию свойств placeholder и attributedPlaceholder.

Если присмотреться, то анимация выполняется в тот момент, когда текст появляется, либо полностью удаляется из поля ввода. Поэтому, в очередной раз, необходимо воспользоваться UITextFieldDelegate методом textField(_:shouldChangeCharactersIn:replacementString). Стоит помнить, что делегат может задаваться и из внешней части программы (например при необходимости форматирования номера телефона). Именно по этой причине нужно отдельно сохранить делегат, задаваемый из внешней части программы.

Заключение

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

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ace Rodstin

Ace Rodstin

More from Medium

SwiftUI’s EnvironmentValues backported to UIKit

SwiftUI: How to declare variables inside GeometryReader

Is Swift the Objective Choice now?

Every iOS developer should know these Xcode tricks