当前位置: > Swift新手入门 >

Swift技术之如何在iOS 8下使用Swift设计一个自定义的输入法

时间:2014-09-10 16:49来源:未知 作者:啊成 举报 点击:562次

我会复习一下有关键盘扩展的内容,然后通过使用iOS 8中的新应用扩展API的设计一个摩斯码的输入法。完成这个教程大约需要花费20分钟。完整代码

概览

通过使用自定义输入法替换系统输入法,用户可以实现一些特别的功能。例如一个特别新颖的输入方式,或输入iOS原生并不支持的语言。自定义输入法的基本功能很简单:通过点击、手势,或者其他输入事件,然后通过一个未分类的 NSString 对象在当前文本输入对象的文本插入点插入文字。

当用户选择了某个输入法后,当应用打开时,它将会变为默认的输入法。因此,任意输入法都应该允许用户切换到另一个输入法。

对于每一个自定义输入法,有两个很重要的点:
1、信任: 你的自定义输入法能够让你访问用户输入的所有内容,因此你和你的用户之间的信任非常重要。
2、一个“下一个输入法”选项: 兼容能够让用户切换至另一个输入法必须成为每个自定义输入法界面的一部分。你必须在你的界面中中提供。

自定义输入法所不能实现的

有一些输入对象是自定义输入法不能被使用的情况:安全字段(例如密码),电话号码对象(就像联系人应用中的号码字段)。

你的自定义输入法不能访问输入框的视图层级,它也不能控制光标和选择文本。

同样,自定义输入法不能在顶行之外显示任何内容(就像系统键盘当你在后面的行上长按一个键时)。

沙盒

默认情况下,自定义输入法并没有网络访问,也不能和容纳它的应用共享文件。如果想实现这些功能,必须在Info.plist文件中将RequestOpenAccess布尔值至YES。做了这个之后,会扩展自定义输入法的沙盒,就像在建立和维护用户信任提到的那样。

如果你确实需要申请访问权限,你的输入法将获得以下权限,每一个都会有相应的责任:

  • 访问位置服务和地址本数据库,每一个都需在第一次访问时获得授权。
  • 可选择与容纳该输入法的应用的共享容器,使得在应用中可以定制词汇表。
  • 能够将输入的字符和其他输入事件上传至服务器进行处理。
  • 访问iCloud,例如,能够确保输入法的设置以及你的自动修正词汇能够在所有用户设备上同步。
  • 通过包含还输入法的应用,能够访问Game Center和应用内购买。
  • 能够和受控应用进行协同,如果你使用来设计该键盘以支持移动设备管理(MDM)

顶层设计

接下来的图形展示了在运行的输入法下有哪些重要的元素,同时展示了在一个典型的开发流程中,他们的位置。在大部分情况下,我们通过一个应用来容纳这个输入法扩展,一个UIInputViewController来控制这个键盘并针对用户事件给予反馈。

自定义的输入法模板包含一个UIInputViewController的子类,这个就是你的输入法的主要视图控制器。让我们看一下他的接口来熟悉一下是如何实现的:

 
 
class UIInputViewController : UIViewController, UITextInputDelegate, NSObjectProtocol {
 
var inputView: UIInputView!
 
var textDocumentProxy: NSObject! { get }
 
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)!)
}
  • UIInputViewController支持UITextInputDelegate协议,当文本区或者文本选项区变化时,通过selectionWillChange,selectionDidChange,textWillChange和textDidChange事件来实现。

设计一个摩斯码输入法

我们会设计实现一个简单的输入法,可以输入 点 和 划,改变了键盘结构,删除字符然后隐藏自己。这个范例通过代码来生成的用户界面。当然,我们同样也可以使用Nib文件来生成界面-这个会在教程的末尾涉及。加载Nib文件会对性能有负面影响。

创建一个工程

打开Xcode6 ,创建一个“Single Page Application”,然后选择Swift为编程语言。

添加一个文本区域

打开Main.storyboard然后拖拽一个文本区域从组件库里。我们会使用这个来测试我们设计的键盘。

将这个文本区域居中,然后添加必要的contraints。

Hint: 如果你调用textField.becomeFirstResponder()在viewDidLoad,这个键盘会自动在应用打开时弹出。

添加这个键盘扩展

从导航器中选择这个项目文件,然后通过按+按钮添加一个新的target。

选择Application Extension然后使用Custom Keyboard模板,命名它为MorseCodeKeyboard。

这会创建一个名为MorseCodeKeyboard新文件夹,包括2个文件KeyboardViewController.swift和Info.Plist。

接下来

打开KeyboardViewController.swift,为了在不同的键盘之间进行切换,这个键盘模板文件会有一个按钮。在viewDidLoad方法中放入一个新的方法,命名为addNextKeyboardButton。

 
 
func addNextKeyboardButton() {
    self.nextKeyboardButton = UIButton.buttonWithType(.System) as UIButton
 
    ...
 
    var nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: -10.0)
    self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint])
}

为了更好的梳理代码的结构,创建一个新的方法名为addKeyboardButtons,然后在viewDidLoad中调用它。虽然这里只有几个按钮,但是在真实项目中,将会有更多的按钮。在addKeyboardButtons中调用addNextKeyboardButton。

 
 
class KeyboardViewController: UIInputViewController {
 
...
 
override func viewDidLoad() {
    super.viewDidLoad()
 
    addKeyboardButtons()
}
 
func addKeyboardButtons() {
    addNextKeyboardButton()
}
 
...
 
}

现在我们来添加点按钮,创建一个UIButton!类型的dotButton属性。

 
 
class KeyboardViewController: UIInputViewController {
 
var nextKeyboardButton: UIButton!
var dotButton: UIButton!
 
...
}

增加一个名为addDot的方法。使用系统按钮来初始化名为dotButton的属性。增加一个TouchUpInside事件回调函数。设置一个大字体然后增加圆角,同时增加约束来限制它距离水平中心50个points,垂直居中。这个代码应该和下面nextKeyboardButton部分类似。

 
 
func addDot() {
// initialize the button
dotButton = UIButton.buttonWithType(.System) as UIButton
dotButton.setTitle(".", forState: .Normal)
dotButton.sizeToFit()
dotButton.setTranslatesAutoresizingMaskIntoConstraints(false)
 
// adding a callback
dotButton.addTarget(self, action: "didTapDot", forControlEvents: .TouchUpInside)
 
// make the font bigger
dotButton.titleLabel.font = UIFont.systemFontOfSize(32)
 
// add rounded corners
dotButton.backgroundColor = UIColor(white: 0.9, alpha: 1)
dotButton.layer.cornerRadius = 5
 
view.addSubview(dotButton)
 
// makes the vertical centers equa;
var dotCenterYConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0)
 
// set the button 50 points to the left (-) of the horizontal center
var dotCenterXConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: -50)
 
view.addConstraints([dotCenterXConstraint, dotCenterYConstraint])
}

通过使用textDocumentProxy来实现dotButton回调函数。

 
 
func didTapDot() {
var proxy = textDocumentProxy as UITextDocumentProxy
 
proxy.insertText(".")
}

在addKeyboardButtons中调用addDot方法。

 
 
func addKeyboardButtons() {
addDot()
 
addNextKeyboardButton()
}

接下来对于dash,delete和hideKeyboard,这个过程都比较类似。这个deleteButton会从proxy使用deleteBackward方法,然后hideKeyboardButton会通过KeyboardViewController使用dismissKeyboard方法。

与dash相关的代码几乎和dotButton代码一致。为了将dashButton按钮在水平方向与 点 按钮对称,只要将水平约束中的常量改变一下符号即可。

 
 
func addDash() {
...
 
// set the button 50 points to the left (-) of the horizontal center
var dotCenterXConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: -50)
 
view.addConstraints([dashCenterXConstraint, dashCenterYConstraint])
 
 
func didTapDash() {
var proxy = textDocumentProxy as UITextDocumentProxy
 
proxy.insertText("_")
}

回删

当被按下时,这个删除按钮会通过textDocumentProxy,使用deleteBackword删除字符。这个布局约束会和nextKeyboardButton对称(.Left -> .Right, .Bottom->.Top)

 
 
func addDelete() {
deleteButton = UIButton.buttonWithType(.System) as UIButton
deleteButton.setTitle(" Delete ", forState: .Normal)
deleteButton.sizeToFit()
deleteButton.setTranslatesAutoresizingMaskIntoConstraints(false)
deleteButton.addTarget(self, action: "didTapDelete", forControlEvents: .TouchUpInside)
 
deleteButton.backgroundColor = UIColor(white: 0.9, alpha: 1)
deleteButton.layer.cornerRadius = 5
 
view.addSubview(deleteButton)
 
var rightSideConstraint = NSLayoutConstraint(item: deleteButton, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: -10.0)
 
var topConstraint = NSLayoutConstraint(item: deleteButton, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: +10.0)
 
view.addConstraints([rightSideConstraint, topConstraint])
 
 
func didTapDelete() {
var proxy = textDocumentProxy as UITextDocumentProxy
 
proxy.deleteBackward()
}

隐藏键盘

当被按下时,这个hideKeyboardButton会在KeyboardViewController上调用dismissKeyboard。

 
 
func addHideKeyboardButton() {
hideKeyboardButton = UIButton.buttonWithType(.System) as UIButton
 
hideKeyboardButton.setTitle("Hide Keyboard", forState: .Normal)
hideKeyboardButton.sizeToFit()
hideKeyboardButton.setTranslatesAutoresizingMaskIntoConstraints(false)
 
hideKeyboardButton.addTarget(self, action: "dismissKeyboard", forControlEvents: .TouchUpInside)
 
view.addSubview(hideKeyboardButton)
 
var rightSideConstraint = NSLayoutConstraint(item: hideKeyboardButton, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: -10.0)
 
var bottomConstraint = NSLayoutConstraint(item: hideKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: -10.0)
 
view.addConstraints([rightSideConstraint, bottomConstraint])
}

使用Nib文件

如果写约束并非你擅长的方式,你可以创建一个界面文件,然后将它添加到你的inputView。

创建一个界面文件

右击MorseCodeKeyboard文件组,然后选择创建新文件。

选择User Interface,然后View Template,命名为CustomKeyboardInterface。

选择File’s Owner,然后在Identity Inspector标签下,将类的名字改为KeyboardViewController。

在视图中加入一个按钮,然后将Title设置为We ❤ Swift。这个界面应该和这个有点相似:

加载界面

在init(nibName, bundle)构造函数内,加载这个CustomKeyboard文件,然后保存一个对于这个界面的引用。

 
 
class KeyboardViewController: UIInputViewController {
 
...
 
var customInterface: UIView!
 
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
 
    var nib = UINib(nibName: "CustomKeyBoardInterface", bundle: nil)
    let objects = nib.instantiateWithOwner(self, options: nil)
    customInterface = objects[0] as UIView
}
 
...
 
}

将它添加到inputView

在viewDidLoad方法中,将这个自定义界面添加到inputView。

 
 
class KeyboardViewController: UIInputViewController {
 
...
 
override func viewDidLoad() {
    super.viewDidLoad()
 
    view.addSubview(customInterface)
 
    ...
}
 
...
 
}

为按钮添加回调函数

 
 
class KeyboardViewController: UIInputViewController {
 
...
 
@IBAction func didTapWeheartSwift() {
    var proxy = textDocumentProxy as UITextDocumentProxy
 
    proxy.insertText("We ❤ Swift")
}
 
...
 
}

将按钮事件绑定到回调函数

在按钮上右击,然后单击并拖拽touchUpInse事件到didTapWeHeartSwift IBAction。

最后,这个代码应该和这个差不多。

在你的设备上安装应用

在设备上运行你的项目。他会增加一个自定义键盘在你的设备上,但是在你使用它之前,你必须安装它。

进入设置>通用。选择键盘-应该在选项的底部。

选择键盘。

选择增加新键盘。然后你会看到MorseCode的键盘,选择然后安装这个键盘。

现在我们可以再次运行这个应用,然后使用你的键盘了。

Swift技术之如何在iOS 8下使用Swift设计一个自定义的输入法 (主要是NSLayoutConstraint 的使用)的更多相关文章

  1. 如何在 iOS 8 中使用 Swift 实现本地通知(上)

    当你的应用在后台运行时,可以简单地使用本地通知把信息呈现给用户.它可以允许你显示 提醒.播放提示音和数字角标(badge).本地通知可以被以下的事件触发:计划好的时间点或者用户进入和离开某个地理区域. ...

  2. 如何在 iOS 8 中使用 Swift 实现本地通知(下)

    在上集中,我们已经构建了一个简单的待办列表应用(to-do list app),这个应用可以在待办项过期时通过本地通知提醒用户.现在,我们要在之前的基础上添加以下功能:应用图标角标上显示过期待办项的数 ...

  3. 如何在Room框架下注册onUpgrade回调及自定义DatabaseErrorHandler

      在 Android 中,Room 为 SQLite 提供了高效稳定的抽象层,简化开发流程.RoomDatabase.java 是初始化数据库的重要构建组件,通过它我们可以添加RoomDatabas ...

  4. 在一个ros包下怎么使用另外一个自定义ros包里的message

    假设自定义消息包my_message_package https://answers.ros.org/question/206257/catkin-use-ros-message-from-anoth ...

  5. IOS中使用.xib文件封装一个自定义View

    1.新建一个继承UIView的自定义view,假设类名叫做 MyAppVew #import <UIKit/UIKit.h> @class MyApp; @interface MyAppV ...

  6. 剑指offer系列——二维数组中,每行从左到右递增,每列从上到下递增,设计算法找其中的一个数

    题目:二维数组中,每行从左到右递增,每列从上到下递增,设计一个算法,找其中的一个数 分析: 二维数组这里把它看作一个矩形结构,如图所示: 1 2 8 2 4 9 12 4 7 10 13 6 8 11 ...

  7. fir.im Weekly - 如何在 iOS 上构建 TensorFlow 应用

    本期 fir.im Weekly 收集了最近新鲜出炉的 iOS /Android 技术分享,包括 iOS 系统开发 TensorFlow 教程.iOS 新架构.iOS Notifications 推送 ...

  8. iOS Swift-简单值(The Swift Programming Language)

    iOS Swift-简单值(The Swift Programming Language) 常量的声明:let 在不指定类型的情况下声明的类型和所初始化的类型相同. //没有指定类型,但是初始化的值为 ...

  9. 如何在iOS地图上高效的显示大量数据

    2016-01-13 / 23:02:13 刚才在微信上看到这篇由cocoachina翻译小组成员翻译的文章,觉得还是挺值得参考的,因此转载至此,原文请移步:http://robots.thought ...

随机推荐

  1. PHP 防范xss攻击

    XSS 全称为 Cross Site Scripting,用户在表单中有意或无意输入一些恶意字符,从而破坏页面的表现! 看看常见的恶意字符XSS 输入: 1.XSS 输入通常包含 JavaScript ...

  2. C#_WinForm接收命令行参数

    C#_WinForm接收命令行参数 2014-08-03 10:17 534人阅读 评论(0) 收藏 举报 首先,我要仔细的声明下,本文讲的是接受命令行参数,让程序启动.而不是启动那个黑黑的框...我 ...

  3. VS2010命令行编译C#和VC项目

    VS2010命令行编译C#和VC项目 VS2010命令行编译C#和VC项目 根据需要动态创建数据库字段后,需要动态创建或者调整页面,那就需要编译这些页面和后台文件.因此使用命令行编译将会非常方便,对于 ...

  4. SQL 面试题及答案(一)

    1. SQL 语法:update set from: http://wenku.baidu.com/link?url=aVr5EbEmx-pNK86rdnas8YDWG8txjg8GEry-HU_dF ...

  5. python 文件操作,seek, tell, name...

    可以设置文件当前位置偏移 fileObject.seek(offset [, whence]) offset 文件的读/写指针位置 whence 可选,默认0,相对于文件开始的位置. 1, 相对于目前 ...

  6. hdu 2337 Escape from Enemy Territory

    题目大意 给你一张nn*mm矩形地图.上面有些点上有敌营.给你起点和终点, 你找出一条最优路径.满足最优路径上的点离敌营的最近最短距离是所有路径最短的.若有多条找路径最短的一条. 分析 通过二分来确定 ...

  7. vim 把满足条件的数字进行加上一些数字

    1,1,1,n4s-1 1,3,4,n3s= 1,4,6,e4h= 1,5,8,e4h-1 1,6,2,e3ntx-2 1,7,5,n1s+2 1,8,7,n3s=   比如以上的数据格式以“,”为列 ...

  8. Linux系统下查看硬件信息命令大全

    导读 有许多命令可以用来查看 Linux 系统上的硬件信息.有些命令只能够打印出像 CPU 和内存这一特定的硬件组件信息,另外一些命令可以查看多种硬件组件的信息. 这个教程可以带大家快速了解一下查看各 ...

  9. js部分---数组及练习题;

    数据存储--数组: 强类型语言数组 1.同一类型的数据存储的集合,在内存中是连续的 2.定义的时候需要制定长度 弱类型语言数组 1.可以存储任意类型的数据 2.在内存中不连续,不需要制定长度 定义一个 ...

  10. 神奇的输入 while(cin>>....)如何在遇见换行之后进入下一层循环读入

    cin>>m>>n; ;i<=m;i++) { ; char ch=' '; ) //在遇到换行之后进入下一层循环读入. { x++; cin>>c[x]; ...