当前位置: 首页 > 科技观察

iOS8自定义输入法教程:如何创建第三方输入法

时间:2023-03-13 16:42:00 科技观察

iOS8带来了很多很酷的功能,其中之一就是添加第三方输入法作为应用程序扩展。我们应该认真对待这一刻,因为应用程序扩展开启了一类全新的应用程序和付费操作。应用商店中有数百万个应用,这对开发者和用户来说是全新的一天。在本文中,我将向您展示如何为在系统范围内运行的应用程序创建第三方输入法。本教程将在Swift中完成。这是我在Swift中的第一个真正的项目,我喜欢它。现在,让我们直接深入了解如何创建第三方输入法。首先,让我向您展示我们将要构建的输入法的最终渲染图。输入法将能够在文本框中输入和删除文本,并实现其他基本功能。更多高级功能,如上下文预测、字典等,超出了本教程的范围。创建Xcode项目打开Xcode6并创建一个新项目。File->New->Project,将项目命名为CustomKeyboardSample,并保存在合适的位置。这是我们的主要项目,但我们还需要添加扩展。现在,让我们向项目中添加一个文本字段。打开Main.Storyboard文件并将UITextField控件拖放到屏幕上的视图控制器上。这是我们测试输入的地方。现在是时候添加扩展了。点击File->New->Target,在iOS/ApplicationExtension列表中选择CustomKeyboard。将扩展命名为CustomKeyboard并选择Swift编程语言。添加扩展现在你应该有一个名为CustomKeyboard的新目标文件夹,其中包含一个名为KeyboardViewController.swift的文件。Xcode为我们添加了一些初始代码,键盘应该可以工作(尽管功能很少)。现在您可以尝试运行CustomKeyboardSample并尝试您的新输入法。单击文本字段时,将显示系统键盘。我们可以使用底部一行的地球图标来切换输入法,但是这个图标只有在我们安装新键盘后才会出现。转到主屏幕(单击菜单栏,Hardware->Home)。打开设置并转到常规->键盘->键盘。单击“添加新键盘”并选择自定义键盘。打开开关启用它并同意警告信息。单击完成,您就可以开始了!如果应用程序再次在模拟器上运行,则可以切换输入法。单击地球图标,直到您看到只有“下一个键盘”按钮的键盘。现在是时候开始为输入法添加按钮了。打开文件KeyboardViewController.h。在此文件中,您将看到一个继承自UIInputViewController的类KeyboardViewController。这是管理视图的键盘类。我们可以向包含视图添加一个按钮,它将显示在键盘中。添加一个名为createButtonfunccreateButtonWithTitle(title:String)->UIButton{letbutton=UIButton.buttonWithType(.System)asUIButtonbutton.frame=CGRectMake(0,0,20,20)button.setTitle(title,forState:.Normal)button的函数.sizeToFit()button.titleLabel.font=UIFont.systemFontOfSize(15)button.setTranslatesAutoresizingMaskIntoConstraints(false)button.backgroundColor=UIColor(white:1.0,alpha:1.0)button.setTitleColor(UIColor.darkGrayColor(),forState:.Normal)button.addTarget(self,action:"didTapButton:",forControlEvents:.TouchUpInside)returnbutton}在上面的代码中,我们以编程方式添加了一个按钮并设置了它的属性。我们可以用nib文件做到这一点,但我们将不得不管理这么多按钮。所以这是一个更好的选择。代码调用名为didTapButton的方法来响应按下的按钮。现在让我们添加一个方法。funcdidTapButton(sender:AnyObject?){letbutton=senderasUIButtonlettitle=button.titleForState(.Normal)varproxy=textDocumentProxyasUITextDocumentProxyproxy.insertText(title)}在上面的方法中,我们使用swift实现了一个按钮事件的处理。AnyObject类型类似于Objective-C中的ID对象。我们在UIButton上放了一个teleporter,然后获取到按钮的标题文字,也就是我们要在文本框中输入的文字。要使用输入法输入文本,我们使用textDocumentProxy对象并调用insertText方法。下一步是向我们的键盘视图添加一个按钮。在viewDidLoad方法下方添加两行代码。您可以在viewDidLoad和textDidChange方法中删除自动生成的代码。overridefuncviewDidLoad(){super.viewDidLoad()letbutton=createButtonWithTitle("A")self.view.addSubview(button)}添加一个标题为“A”的按钮到输入法。这是我们的钥匙。现在,运行应用程序并单击文本框,您应该会在键盘上看到“A”键。(您可能需要切换键盘)。点击这个按钮,看看会发生什么……看!我们已经有了文本A!好吧,让我们添加更多按钮,让这个孩子看起来像一个真正的IME。让我们修改我们放在viewDidLoad方法中的代码。overridefuncviewDidLoad(){super.viewDidLoad()letbuttonTitles=["Q","W","E","R","T","Y","U","I","O","P"]varbuttons=UIButton[]()varkeyboardRowView=UIView(frame:CGRectMake(0,0,320,50))forbuttonTitleinbuttonTitles{letbutton=createButtonWithTitle(buttonTitle)buttons.append(button)keyboardRowView.addSubview(button)}self.view.addSubview(keyboardRowView)现在使用这段新代码,我们创建了一个包含键标题的数组,我们还创建了这些键的列表。每个键都添加到一个数组和一个UIView,这将是我们的第一个键列。然后将该视图添加到主键映射。如果你运行这个程序,你可能只会看到P键,因为所有的按钮都在同一个地方。我们需要以编程方式添加一些约束,以便它们可以排成一行。因此,我们将创建一个新函数来创建约束。funcaddIndividualButtonConstraints(buttons:UIButton[],mainView:UIView){for(index,button)inenumerate(buttons){vartopConstraint=NSLayoutConstraint(item:button,attribute:.Top,relatedBy:.Equal,toItem:mainView,attribute:.Top,multiplier:1.0,constant:1)varbottomConstraint=NSLayoutConstraint(item:button,attribute:.Bottom,relatedBy:.Equal,toItem:mainView,attribute:.Bottom,multiplier:1.0,constant:-1)varrightConstraint:NSLayoutConstraint!ifindex==buttons.count-1{rightConstraint=NSLayoutConstraint(item:button,attribute:.Right,relatedBy:.Equal,toItem:mainView,attribute:.Right,multiplier:1.0,constant:-1)}else{letnextButton=按钮[index+1]rightConstraint=NSLayoutConstraint(item:button,attribute:.Right,relatedBy:.Equal,toItem:nextButton,attribute:.Left,multiplier:1.0,constant:-1)}varleftConstraint:NSLayoutConstraint!ifindex==0{leftConstraint=NSLayoutConstraint(item:button,attribute:.Left,relatedBy:.Equal,toItem:mainView,attribute:.Left,multiplier:1.0,constant:1)}else{letprevtButton=buttons[index-1]leftConstraint=NSLayoutConstraint(item:button,attribute:.Left,relatedBy:.Equal,toItem:prevtButton,attribute:.Right,multiplier:1.0,constant:1)letfirstButton=按钮[0]varwidthConstraint=NSLayoutConstraint(item:firstButton,attribute:.Width,relatedBy:.Equal,toItem:button,attribute:.Width,multiplier:1.0,constant:0)mainView.addConstraint(widthConstraint)}mainView.addConstraints([topConstraint,bottomConstraint,rightConstraint,leftConstraint])}}这是一段很长的代码,不是吗?使用AutoLayout,你不能真正建立它,你需要一次添加所有约束,否则它不会起作用。上面代码的主要工作是添加边框为1px的约束,它会被添加到每个键的顶部和底部。它还将1px左右约束添加到相邻键的左侧和右侧(如果它是行中的第一个或最后一个键,则添加到行视图)。如果我们在viewDidLoad中添加调用上述函数的功能,我们应该会看到显示的新行按钮。现在,这看起来更像是一种输入法。下一步是为输入法添加额外的行。为此,让我们进行一些快速重构。创建并实现一个新方法,为每行添加键。funccreateRowOfButtons(buttonTitles:NSString[])->UIView{varbuttons=UIButton[]()varkeyboardRowView=UIView(frame:CGRectMake(0,0,320,50))forbuttonTitleinbuttonTitles{letbutton=createButtonWithTitle(buttonTitle)buttons.append(buttonView.keyboardRow)addSubview(button)}addIndividualButtonConstraints(buttons,mainView:keyboardRowView)returnkeyboardRowView}我们基本上已经将代码从viewDidLoad提取到它自己的方法中。这个新方法将标题文本存储在一个数组中,并返回一个包含该行所有按钮的视图。现在,我们可以在要添加的任何代码中调用此代码。所以我们新的vieDidLoad方法看起来像这样:I","O","P"]letbuttonTitles2=["A","S","D","F","G","H","J","K","L"]letbuttonTitles3=["CP","Z","X","C","V","B","N","M","BP"]letbuttonTitles4=["CHG","SPACE",“返回”]varrow1=createRowOfButtons(buttonTitles1)varrow2=createRowOfButtons(buttonTitles2)varrow3=createRowOfButtons(buttonTitles3)varrow4=createRowOfButtons(buttonTitles4)self.view.addSubview(row1)self.view.addSubview(row2)self.view。addSubview(row3)self.view.addSubview(row4)}在上面的代码中,我们添加了4行键,然后将这些键添加到主视图中的行中。我们现在可以运行代码,但您只会看到第一行,因为它们都在同一位置。我们需要添加一些自动布局约束。funcaddConstraintsToInputView(inputView:UIView,rowViews:UIView[]){for(index,rowView)inenumerate(rowViews){varrightSideConstraint=NSLayoutConstraint(item:rowView,attribute:.Right,relatedBy:.Equal,toItem:inputView,attribute:.Right,multiplier:1.0,constant:-1)varleftConstraint=NSLayoutConstraint(item:rowView,attribute:.Left,relatedBy:.Equal,toItem:inputView,attribute:.Left,multiplier:1.0,constant:1)inputView.addConstraints([leftConstraint,rightSideConstraint])vartopConstraint:NSLayoutConstraintifindex==0{topConstraint=NSLayoutConstraint(item:rowView,attribute:.Top,relatedBy:.Equal,toItem:inputView,attribute:.Top,multiplier:1.0,constant:0)}else{letprevRow=rowViews[index-1]topConstraint=NSLayoutConstraint(item:rowView,attribute:.Top,relatedBy:.Equal,toItem:prevRow,attribute:.Bottom,multiplier:1.0,constant:0)letfirstRow=rowViews[0]varheightConstraint=NSLayoutConstraint(item:firstRow,attribute:.Height,relatedBy:.Equal,toItem:rowView,attribute:.Height,multiplier:1.0,constant:0)inputView.addConstraint(heightConstraint)}inputView.addConstraint(topConstraint)varbottomConstraint:NSLayoutConstraintifindex==rowViews.count-1{bottomConstraint=NSLayoutConstraint(item:rowView,attribute:.Bottom,relatedBy:.Equal,toItem:inputView,attribute:.Bottom,multiplier:1.0,constant:0)}else{letnextRow=rowViews[index+1]bottomConstraint=NSLayoutConstraint(item:rowView,attribute:.Bottom,relatedBy:.Equal,toItem:nextRow,attribute:.Top,multiplier:1.0,constant:0)}inputView.addConstraint(bottomConstraint)}}这个新方法做了类似的功能,我们添加了最新的自动布局代码,相对于主视图方向添加了一个1px的约束行的左侧、右侧和底部,并在每行和它上面的行之间添加一个0px约束。现在,我们需要从viewDidLoad方法中调用这段代码。overridefuncviewDidLoad(){super.viewDidLoad()letbuttonTitles1=["Q","W","E","R","T","Y","U","I","O","P"]letbuttonTitles2=["A","S","D","F","G","H","J","K","L"]letbuttonTitles3=["CP","Z","X","C","V","B","N","M","BP"]letbuttonTitles4=["CHG","SPACE","RETURN"]varrow1=createRowOfButtons(buttonTitles1)varrow2=createRowOfButtons(buttonTitles2)varrow3=createRowOfButtons(buttonTitles3)varrow4=createRowOfButtons(buttonTitles4)self.view.addSubview(row1)self.view.addSubview(row2)self.view.addSubview(row3)self.view.addSubview(row4))row1.setTranslatesAutoresizingMaskIntoConstraints(false)row2.setTranslatesAutoresizingMaskIntoConstraints(false)row3.setTranslatesAutoresizingMaskIntoConstraints(false)row4.setTranslatesAutoresizingMaskIntoConstraints(false)addConstraintsToInputView(self.view,rowViews:[row1,row2,row3,row4])}这是我们新的viewDidLoad函数您会看到我们将每一行的TranslatesAutoresizingMaskIntoConstraints设置为false。这是为了确保更好地使用AutoLayout而不是ConstrainedLayout。现在,如果您运行该应用程序,您会看到输入法都按应有的方式排列。您可以单击所有按钮以查看将它们输入到文本框中。还有一个小问题,非文本键不能正常工作(例如退格键、空格键)。要解决此问题,我们需要更改我们的didTapButton方法以添加正确的方式来响应这些键。funcdidTapButton(sender:AnyObject?){letbutton=senderasUIButtonlettitle=button.titleForState(.Normal)asStringvarproxy=textDocumentProxyasUITextDocumentProxyproxy.insertText(title)switchtitle{case"BP":proxy.deleteBackward()case"RETURN":proxy.insertText("\n")case"SPACE":proxy.insertText("")case"CHG":self.advanceToNextInputMode()default:proxy.insertText(title)}}在这里,switch语句用于识别按下的键。退格键调用delegate上的deleteBackward方法,空格键插入一个空格,CHG键改变输入法或系统输入法或安装下一个输入法。下一步是什么?这就是本教程的全部内容,本教程对如何创建基本的自定义键盘进行了粗略的解释。你的作业是进一步完善它,看看你是否可以添加更多高级功能,例如使用CapsLock键添加大写字母、切换到数字/符号键盘方案等。