创建自注册的Swift UI 控件
对于自定义控件来说,在不破坏原有的消息机制的前提下,如何响应事件通知?在本文中,我将演示一个通知代理类,通过一个简单的例子,我们用该类向已有的iOS UI控件中增加了自己的新功能:为Text View控件增加placeholder文本。 问题:缺失的PlaceholderPlaceholder文本是用于在某些控件中提示用户输入指定类型的数据的良好方式。UIKit的UITextField控件的placeholder属性就是用来干这个的。例如,下图中,在Twitter的选项设置中,User Name字段就使用了placeholder文本。通过这个placeholder文本,用户可以知道应当在这里输入一个包含了@符号的Twitter用户名。而在Password字段的placeholder文本则标明该字段是必填的。 尽管Text Filed有placeholder属性,但它的兄弟控件Text View却没有这个属性。 问题的解决我准备这样解决这个问题:
就像我在前面的文章中提到的,我更喜欢使用扩展,而不是子类。因为扩展允许我在App中使用“盒子之外”的UIKit控件。我需要做的仅仅是在项目中加入某个类的扩展文件,然后这个类会自动获得新的特性。 方式1:UITextViewDelegate当用户在TextView中输入文本时,iOS有几种通知App的方式。其中一种就是通过UITextViewDelegate协议。在这个扩展中有一个对TextView的引用保存在它自己的delegate属性中。当用户输入或删除字符时,TextView的协议方法会被调用。我们可以在协议方法中加入隐藏和显示placeholder标签的代码。 这个解决方法的缺点是,Text View太过于依赖delegate属性,因为在一个UIControl中你只能注册一个委托对象。如果你需要向TextView中加入更多的委托对象,你就会比较麻烦。 方式2:NSNotificationCenterNSNotificationCenter通过UITextViewTextDidChangeNotification通知来告诉你用户在TextView中输入或删除了某些字符。这是一种更好的选择,因为它不再依赖于delegate属性。缺点是需要向通知中心进行注销。一般,我们在对象的deinit方法中向NSNotificationCenter注销该对象。但是在Swift中,我们无法在扩展中使用deinit方法。那我们怎样才能突破这个限制呢? 创建一个通知代理我们通过创建一个轻量级的代理对象来突破这个限制,这个代理对象代替TextView来向通知中心进行注册。 我已经在我的项目中创建了一个UITextView扩展以及一个代理类,你可以从这里下载。 双击.xcodeproj文件,用Xcode打开这个项目。在项目导航窗口中选中mmTextViewExtensions.swift文件,你可以找到这个通知代理类(如你所见,其中也包含了上一篇文章中的mmDyamicTypeExtensions类)。 下面是位于文件头部的通知代理类: 你可以看到,它是UIView子类。这样我们就可以将它当成subview添加到TextView中。addObserverForName:usingBLock:方法通知中心的方法拥有一样的签名。这个方法中只有一行代码,就是将TextView注册到通知中心并将参数传递过去,包括TextView的闭包,这样当通知发生时该闭包被执行。通知中心会返回一个NSObjectProtocol对象,稍后我们会用来进行通知的注销操作。 deinit方法也只有一行代码,仅仅是向通知中心进行对象的注销。 通知代理类是可重用的。你可以用于任何类型的通知以及你想接收通知的任何类型的对象。 然后是UITextView的扩展,在扩展中有一个placeholderLabel属性,如你所想,它引用了一个placeholder标签对象。 此外还有一个placeholder的字符串属性。在后面你将看到,所有的奇迹将在运行时发生,当placeholder属性被设置,这个计算属性的代码就会执行。 现在打开Main.storyboard文件。如图2所示,故事板中只有一个Scene,在这个Scene的顶部,有一个Text View。 当你选择Text View,然后打开属性面板,你将看见一个Placeholder属性,它是扩展中增加的属性,这个属性的默认值是“Enter your text here!”。在设计时不会显示这段文本(要在设计时可见,我们需要花费大量的工作,因此这不是本文的主题),但在运行时它会显示。 The Notification Proxy at Run Time运行时的通知代理图3中的UML序列图显示了所有对象之间发生的消息传递的顺序。 图 3 - 运行时对象间消息传递的顺序 每一步的解释如下:
Let’s Take it for a Test Run!运行测试
图 4 - 运行时的 placeholder 文本 4.Now type some code in the text view. As soon as you begin typing,the placeholder label disappears (Figure 5). 现在在TextView中输入任意字符。此时,placeholder将消失(图5)。 图 5 -placeholder 文字消失了 如果将输入的字符删除干净,placeholder文字将重新出现。 结束语要解决编程问题有许多方法。最好的方法是先看一下摆在你目前的选择,然后逐个分析其中的利弊。我希望你能看到本文中序列图的好处。它能让你将对象之间在运行时的交互画出来。我在编写代码时也使用了这个方法。你会发现它们能帮助你找出潜在的问题,更能帮你找出编程问题中的新的、更高效的解决方案。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |