占位符介绍

UITextField上设置多个占位符并UITextField设置动画

占位符是一个源远流长的功能强大的概念-易于被用户识别,具有严格,合理的含义,并为开发人员提供了一种提示用户的简便方法。

但是,有时仅拥有一个占位符是不够的。 有时您想向用户展示可以输入的所有内容。 例如,如果您有一个名为“活动标题”的字段,那么您当然可以在其中将“运行”作为占位符。 或者,您可以定义一组占位符,例如“运行中”,“行走”,“行进”,“拳击”,并对它们进行动画处理。 如果认真使用此技术,则可以提供出色的用户体验。 看起来像这样:

占位符库允许您做到这一点。 实际上,它确实非常简单,我鼓励您自己尝试实现类似的东西-它可以很好地介绍迭代器, CATransitionTimer (以前称为NSTimer )。 在本文中,我们将快速浏览API,然后讨论一些体系结构选择和功能。

在开始之前,这里是GitHub上的占位符

德雷蒙德/占位符
占位符–🅿️为UITextField定义多个占位符,并对它们的更改进行动画处理 github.com

现在让我们潜入吧!

如何使用

占位符旨在与视图控制器很好地配合使用。 API的核心部分是Placeholders类。 您可以直接创建它:

  let占位符=占位符(占位符:[“正在运行”,“正在行走”,“骑自行车”]) 

这将创建一组三个占位符。 当然,通常您希望“循环”占位符,以便动画永远运行。 或者您想改组您的集合,以使用户不会每次都以相同的顺序看到相同的占位符。 为此,应分别使用.infinite.shuffled选项,如下所示:

  let占位符=占位符(占位符:[“正在运行”,“正在行走”,“骑自行车”],选项:[.infinite,.shuffle]) 

然后,在viewWillAppear方法中,应将Placeholder实例绑定到UITextField 。 您可以通过调用start方法来做到这一点:

  placeholders.start(时间间隔:3.0, 
fireInitial:正确,
textField:textField,
动画:.pushTransition(.fromBottom))

fireInitial用于立即设置第一个占位符)

基本上就是这样! 现在,您有多个动画占位符。 您可以间隔播放或调整动画以适合您的需求。 如果要创建完全自定义的动画,请查看自述文件。

怎么运行的

总的来说,该解决方案非常简单:它是Timer ,迭代器和CATransition的组合。 数据源是通过AnyIterator提供的,占位符的更改是通过Timer安排的,并使用简单的CATransition执行。

如果您想知道动画的执行方式,则为:

 让过渡= CATransition() 
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(名称:kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromBottom
textField.subviews.first(其中:{NSStringFromClass(type(of:$ 0))==“ UITextFieldLabel”})?. layer.add(transition,forKey:nil)
textField.placeholder = nextPlaceholder

(免责声明:这不是直接来自库的代码-实际的代码被拆分为多个函数,但是为了简单起见,我将其全部放在此处。)

怎么做的

在开发Placeholder时 ,需要做出一些有趣的架构决策。 我想在本文中分享其中的一些。

1.组成而不是继承

通常,这种附加功能是通过子类完成的。 当然,可以简单地创建MultiplePlaceholdersTextField类,并向其添加一些美观的setPlaceholders方法。 但是,但是,它使您无法使用UITextField所有自定义子类(或者更有可能的是,您根本不会使用该库)。 这也降低了代码的可重用性,并且总体而言,这是一个可疑的体系结构决策:此逻辑根本不属于View层。

这就是为什么Placeholders是一个单独的对象的原因,它位于Controller层(据我所相信属于该层)中并且可以与任何UITextField一起使用,因此即使与现有代码库集成起来也很容易。

2.隔离实施

如果查看Placeholder类,您会发现它对UITextField完全一无所知(甚至不导入UIKit )。 而且所有UITextField功能都放在一个扩展中,该扩展实现了前所未有的自定义级别。 基本上,您几乎可以在每个步骤上自定义过程。

Placeholder对象本身只有一个start方法:

  func start(interval:TimeInterval,fireInitial:Bool,action:@escaping(Element)->()) 

在用法部分描述的UITextField方法只是一个包装器。 这有效地分离了逻辑,使实际的实现更简单,更易于管理。

您可以在此处阅读有关此方法背后思想的更多信息。

3.迭代器代替数组

AnyIteratorPlaceholders使用AnyIterator而不是简单的数组。 这样做有两个原因:

  1. 拥有迭代器就足够了。 我们不需要这里配备齐全的阵列。 我们只需要一些可以给我们下一个要素的东西。 迭代器实际上就是那个“东西”。
  2. 可以以有趣的方式修改迭代器。 例如,“无限”功能可在迭代器级别上使用,而不是嵌入到Placeholders 。 您可以在此处检查InfiniteIterator的实现。

4.泛型

您可能没有注意到它,但是Placeholders实际上是一个泛型类型。 这是为什么? 因为这样您可以使用相同的语法使用StringNSAttributedString