Статьи

iOS 8: создание пользовательской клавиатуры в Swift

Начиная с iOS 8, ваши приложения могут расширять пользовательские функции и контент за пределы вашего приложения и делать его доступным для пользователей, когда они используют другие приложения или операционную систему. Одним из способов расширения операционной системы является создание собственной клавиатуры.

В этом уроке я покажу вам, как создать свою собственную клавиатуру с помощью Swift и новых API расширений приложений. Прежде чем мы это сделаем, мы рассмотрим, что может делать расширение для клавиатуры, что оно не может делать и что нужно получить для одобрения в App Store.

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

Основная функция пользовательской клавиатуры проста, реагирует на нажатия, жесты или другие события ввода и предоставляет текст в виде неназначенного объекта NSString в точке вставки текста текущего объекта ввода текста.

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

Для каждой настраиваемой клавиатуры есть две основы разработки:

Доверять. Ваша пользовательская клавиатура дает вам доступ к тому, что печатает пользователь, поэтому доверие между вами и вашим пользователем является существенным.

Клавиша «Следующая клавиатура». Возможность, позволяющая пользователю переключаться на другую клавиатуру, является частью пользовательского интерфейса клавиатуры; Вы должны предоставить один на клавиатуре. Руководство по программированию расширения приложения

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

Существуют определенные объекты ввода текста, которые ваша пользовательская клавиатура не может набирать. К ним относятся защищенные текстовые поля для ввода паролей и объекты телефонной панели, например поля телефонных номеров в приложении « Контакты» .

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

Красная линия показывает верхний предел пользовательской клавиатуры.

По умолчанию клавиатура не имеет доступа к сети и не может обмениваться файлами со своим приложением. Чтобы включить эти возможности, установите значение ключа RequestsOpenAccess в файле Info.plist на YES . Это расширяет «песочницу» клавиатуры, как описано в Руководстве по программированию расширений приложений Apple.

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

  • доступ к службам определения местоположения и базе данных адресной книги, каждая из которых требует разрешения пользователя при первом доступе
  • возможность использовать общий контейнер с приложением, содержащим клавиатуру, который включает такие функции, как предоставление пользовательского интерфейса управления лексикой в ​​содержащем приложении
  • возможность отправлять нажатия клавиш и другие входные события для обработки на стороне сервера
  • доступ к iCloud, который вы можете использовать, например, для обеспечения актуальности настроек клавиатуры и вашей пользовательской лексики автозамены на всех устройствах, принадлежащих пользователю
  • доступ к Game Center и покупка из приложения через содержащее приложение
  • возможность работы с управляемыми приложениями, если вы разрабатываете клавиатуру для поддержки управления мобильными устройствами (MDM)

Обязательно прочитайте документ Apple Designing for User Trust , в котором описана ваша ответственность за соблюдение и защиту пользовательских данных в случае, если вы запрашиваете открытый доступ.

В самой простой форме у нас есть приложение, которое содержит расширение клавиатуры и UIInputViewController который управляет клавиатурой и реагирует на пользовательские события.

Шаблон Custom Keyboard содержит подкласс UIInputViewController , который является основным контроллером представления вашей клавиатуры. Давайте посмотрим на интерфейс, чтобы понять, как он работает.

01
02
03
04
05
06
07
08
09
10
11
12
13
class UIInputViewController : UIViewController, UITextInputDelegate, NSObjectProtocol {
 
    var inputView: UIInputView!
 
    var textDocumentProxy: NSObject!
 
    func dismissKeyboard()
    func advanceToNextInputMode()
 
    // This will not provide a complete repository of a language’s vocabulary.
    // It is solely intended to supplement existing lexicons.
    func requestSupplementaryLexiconWithCompletion(completionHandler: ((UILexicon!) -> Void)!)
}
  • inputView — это вид, используемый для клавиатуры, он совпадает со свойством view
  • метод dismissKeyboard может быть вызван для отклонения клавиатуры
  • advanceToNextInputMode используется для переключения между клавиатурами
  • textDocumentProxy — это объект, который вы будете использовать для взаимодействия с текущим вводом текста
1
2
3
self.textDocumentProxy.insertText(«Tuts+») // inserts the string «Tuts+» at the insertion point
 
self.textDocumentProxy.deleteBackward() // Deletes the character to the left of the insertion point
  • UIInputViewController соответствует протоколу UITextInputDelegate , уведомляя вас об изменении текста или выделения текста с помощью событий selectionWillChange , selectionDidChange , textWillChange и textDidChange

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

Откройте Xcode 6, создайте новое приложение Single View и выберите Swift в качестве языка программирования. Назовите это CalculatorKeyboard .

Откройте Main.storyboard и перетащите текстовое поле из библиотеки объектов . Мы будем использовать это для проверки клавиатуры позже. Отцентрируйте текстовое поле и добавьте необходимые ограничения макета, как показано ниже.

Если вы вызовите textField.becomeFirstResponder() в viewDidLoad клавиатура откроется при запуске приложения.

Выберите файл проекта в Навигаторе проектов и добавьте новую цель, нажав кнопку «плюс» внизу.

Выберите расширение приложения слева, выберите шаблон пользовательской клавиатуры и назовите его « Калькулятор» .

Это создаст новую группу с именем Calculator , содержащую два файла KeyboardViewController.swift и Info.plist .

Откройте KeyboardViewController.swift . Клавиатура шаблона имеет одну кнопку, позволяющую пользователю переключаться между клавиатурами. Удалите код в методе viewDidLoad .

Щелкните правой кнопкой мыши группу « Калькулятор » и выберите « Новый файл» . Выберите раздел « Интерфейс пользователя » слева, выберите шаблон « Вид» и назовите его « Калькулятор» . Это должно создать файл с именем Calculator.xib .

Откройте файл XIB и в инспекторе атрибутов справа установите для размера значение « Свободная форма», а для строки состояния — « Нет» .

В инспекторе размера установите ширину вида 320 а высоту 160 .

Перетащите кнопку из библиотеки объектов в представление. В инспекторе атрибутов установите заголовок равным 1 . В инспекторе размера установите ширину и высоту кнопки равными 30 . Переместите кнопку в верхний правый угол представления, пока она не выровняется с полями.

Скопируйте кнопку, нажав и перетащив ее, одновременно нажимая клавишу параметров. Расположите вторую кнопку под первой.

Выберите кнопки, нажав Command-A и скопируйте кнопки. Расположите новые кнопки под первой и второй кнопкой.

Повторите процесс, чтобы создать еще один столбец кнопок, пока у вас не будет четырех столбцов кнопок.


Затем выберите столбец слева и сделайте копию, которая выравнивается по левой границе вида.

Установите ширину кнопок до 140 точек. Замените верхнюю левую кнопку с меткой, размер которой совпадает с размером кнопки. Переименуйте кнопки, как на скриншоте ниже.

Придайте виду синий цвет фона и установите белый цвет фона кнопок с непрозрачностью 15%. А для метки дисплея сделайте его черным с непрозрачностью 15%. Установите размер текста в 18 точек для каждого объекта пользовательского интерфейса и установите цвет текста на белый. Теперь пользовательский интерфейс должен выглядеть так:

Сначала нам нужно создать свойство для хранения пользовательского интерфейса.

1
2
3
4
5
class KeyboardViewController: UIInputViewController {
    var calculatorView: UIView!
 
    …
}

Создайте метод с именем loadInterface и вызовите его в методе viewDidLoad KeyboardViewController .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class KeyboardViewController: UIInputViewController {
    …
 
    override func viewDidLoad() {
        super.viewDidLoad()
 
        loadInterface()
    }
 
    func loadInterface() {
        // load the nib file
        var calculatorNib = UINib(nibName: «Calculator», bundle: nil)
        // instantiate the view
        calculatorView = calculatorNib.instantiateWithOwner(self, options: nil)[0] as UIView
 
        // add the interface to the main view
        view.addSubview(calculatorView)
 
        // copy the background color
        view.backgroundColor = calculatorView.backgroundColor
    }
 
    …
}

На этом этапе вы сможете протестировать новую клавиатуру. Выбрав схему CalculatorKeyboard , соберите и запустите приложение на своем устройстве. Это добавит новую клавиатуру на ваше устройство. Однако, прежде чем вы сможете использовать его, вам сначала нужно установить его.

Перейдите в « Настройки» > « Основные» > « Клавиатура» > « Клавиатуры» и выберите « Добавить новую клавиатуру» . Там вы найдете клавиатуру калькулятора в списке сторонних клавиатур. Выберите и установите клавиатуру. В следующий раз, когда вы откроете клавиатуру, вы сможете увидеть новую клавиатуру, нажав кнопку следующей клавиатуры.

Если вы используете симулятор iOS, пользовательская клавиатура может не работать внутри вашего приложения. Чтобы увидеть клавиатуру, нажмите домой и откройте Spotlight.

Создайте свойство для следующей кнопки KeyboardViewController классе KeyboardViewController .

1
2
3
4
5
6
class KeyboardViewController: UIInputViewController {
 
    @IBOutlet var nextKeyboardButton: UIButton!
 
    …
}

Откройте Calculator.xib , выберите «Владелец файла» , а в инспекторе удостоверений измените его класс на KeyboardViewController .

Щелкните правой кнопкой мыши на кнопке « Следующая клавиатура» и подключите источник ссылки к владельцу файла .

В методе loadInterface мы добавляем действие к кнопке nextKeyboard , как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
class KeyboardViewController: UIInputViewController {
    …
 
    func loadInterface() {
        …
 
        // This will make the button call advanceToNextInputMode() when tapped
        nextKeyboardButton.addTarget(self, action: «advanceToNextInputMode», forControlEvents: .TouchUpInside)
    }
 
}

Создайте свойство для отображения и подключите выходную ссылку в Интерфейсном Разработчике.

1
2
3
4
5
6
class KeyboardViewController: UIInputViewController {
 
    @IBOutlet var display: UILabel!
 
    …
}

Создайте метод с именем clearDisplay и вызовите его в методе viewDidLoad после вызова loadInterface . Дисплей должен показывать 0 при открытии клавиатуры.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
class KeyboardViewController: UIInputViewController {
    …
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        loadInterface()
        clearDisplay()
    }
     
    …
     
    @IBAction func clearDisplay() {
        display.text = «0»
    }
}

Подсоедините событие касания кнопки C к событию clearDisplay в Интерфейсном clearDisplay .

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
class KeyboardViewController: UIInputViewController {
    var shouldClearDisplayBeforeInserting = true
 
    …
 
    @IBAction func didTapNumber(number: UIButton) {
        if shouldClearDisplayBeforeInserting {
            display.text = «»
            shouldClearDisplayBeforeInserting = false
        }
 
        if var numberAsString = number.titleLabel?.text {
            var numberAsNSString = numberAsString as NSString
            if var oldDisplay = display?.text!
                display.text = «\(oldDisplay)\(numberAsNSString.intValue)»
            } else {
                display.text = «\(numberAsNSString.intValue)»
            }
        }
    }
}

Обновите метод clearDisplay как показано ниже.

1
2
3
4
5
6
7
8
class KeyboardViewController: UIInputViewController {
    …
 
    @IBAction func clearDisplay() {
        display.text = «0»
        shouldClearDisplayBeforeInserting = true
    }
}

Код клавиатуры находится в другом месте, чем ваше приложение. Из-за этого журналы отладки не видны. Чтобы просмотреть журналы для цели « Калькулятор» , откройте системный журнал из симулятора iOS.

Кнопка для вставки точки должна добавить точку на дисплей, но только если точки еще нет.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
class KeyboardViewController: UIInputViewController {
    …
 
    @IBAction func didTapDot() {
        if let input = display?.text {
            var hasDot = false
            for ch in input.unicodeScalars {
                if ch == «.»
                    hasDot = true
                    break
                }
            }
            if hasDot == false {
                display.text = «\(input).»
            }
        }
    }
}

Кнопка для вставки текста должна добавить отображаемый текст калькулятора к точке вставки. Для этого мы используем свойство textDocumentProxy как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
class KeyboardViewController: UIInputViewController {
    …
 
    @IBAction func didTapInsert() {
        var proxy = textDocumentProxy as UITextDocumentProxy
 
        if let input = display?.text as String?
            proxy.insertText(input)
        }
    }
}

Поскольку мы реализуем простую клавиатуру, которая не поддерживает деревья выражений , 1 + 2 * 3 будет равно 9 . Мы собираемся использовать более простую модель, в которой калькулятор имеет слот внутренней памяти, к которому он может применять операции.

Давайте сделаем простой ввод, чтобы понять, как работает алгоритм калькулятора:

  • пользователь нажимает 1 , дисплей должен измениться с 0 на 1
  • пользователь нажимает + , калькулятор должен помнить, чтобы добавить следующий введенный номер к 1
  • пользователь нажимает 2 , дисплей должен измениться с 1 на 2
  • пользователь нажимает * , дисплей и внутренняя память калькулятора должны измениться на 3 , калькулятор должен помнить, чтобы умножить внутреннюю память на следующее введенное число
  • пользователь нажимает 3 , дисплей должен оставаться 3
  • пользователь нажимает = , калькулятор должен применить последнюю операцию, и дисплей должен измениться на 9

Замечания:

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

Для реализации калькулятора нам понадобятся:

  • свойство internalMemory котором хранится временный результат
  • свойство, которое хранит nextOperation
  • еще один, чтобы запомнить, следует ли применять nextOperation после нажатия операции
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
enum Operation {
    case Addition
    case Multiplication
    case Subtraction
    case Division
    case None
}
 
class KeyboardViewController: UIInputViewController {
    var internalMemory = 0.0
    var nextOperation = Operation.None
    var shouldCompute = false
 
    …
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class KeyboardViewController: UIInputViewController {
    …
 
    @IBAction func didTapOperation(operation: UIButton) {
        if shouldCompute {
            computeLastOperation()
        }
 
        if var op = operation.titleLabel?.text {
            switch op {
                case «+»:
                    nextOperation = Operation.Addition
                case «-«:
                    nextOperation = Operation.Subtraction
                case «X»:
                    nextOperation = Operation.Multiplication
                case «%»:
                    nextOperation = Operation.Division
                default:
                    nextOperation = Operation.None
            }
        }
    }
}

Создать и реализовать метод computeLastOperation .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class KeyboardViewController: UIInputViewController {
    …
 
    @IBAction func computeLastOperation() {
        // remember not to compute if another operation is pressed without inputing another number first
        shouldCompute = false
 
        if var input = display?.text {
            var inputAsDouble = (input as NSString).doubleValue
            var result = 0.0
 
            // apply the operation
            switch nextOperation {
            case .Addition:
                result = internalMemory + inputAsDouble
            case .Subtraction:
                result = internalMemory — inputAsDouble
            case .Multiplication:
                result = internalMemory * inputAsDouble
            case .Division:
                result = internalMemory / inputAsDouble
            default:
                result = 0.0
            }
 
            nextOperation = Operation.None
 
            var output = «\(result)»
 
            // if the result is an integer don’t show the decimal point
            if output.hasSuffix(«.0») {
                output = «\(Int(result))»
            }
 
            // truncatingg to last five digits
            var components = output.componentsSeparatedByString(«.»)
            if components.count >= 2 {
                var beforePoint = components[0]
                var afterPoint = components[1]
                if afterPoint.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 5 {
                    let index: String.Index = advance(afterPoint.startIndex, 5)
                    afterPoint = afterPoint.substringToIndex(index)
                }
                output = beforePoint + «.»
            }
 
 
            // update the display
            display.text = output
 
            // save the result
            internalMemory = result
 
            // remember to clear the display before inserting a new number
            shouldClearDisplayBeforeInserting = true
        }
    }
}

Обновите метод clearDisplayMethod как показано ниже. Когда пользователь начинает писать первое число, внутренняя память должна быть установлена ​​на 0 а nextOperation должна быть добавлена. Таким образом, после того, как пользователь введет первое число и нажмет операцию, калькулятор запомнит введенное число.

01
02
03
04
05
06
07
08
09
10
class KeyboardViewController: UIInputViewController {
    …
 
    @IBAction func clearDisplay() {
        display.text = «0»
        internalMemory = 0
        nextOperation = Operation.Addition
        shouldClearDisplayBeforeInserting = true
    }
}

Давайте используем атрибут объявления IBInspectable, чтобы добавить угловой радиус для кнопок и отображения. Сначала создайте подкласс UIButton и UILabel .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
class RoundButton: UIButton {
    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
}
 
class RoundLabel: UILabel {
    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
}

В Интерфейсном RoundButton выберите кнопки и измените их класс на RoundButton в Инспекторе Идентификации . В инспекторе Атрибутов вы должны увидеть новый атрибут радиуса угла.

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

Теперь вы должны иметь возможность создать собственную клавиатуру в iOS с помощью API расширений приложения. Помните, что каждая настраиваемая клавиатура должна иметь способ переключения на следующую клавиатуру и что ваша клавиатура не может подключаться к Интернету, получать доступ к службам определения местоположения или общаться с приложением по умолчанию, но вы можете запросить эти возможности.

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