Apache License 2.0

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

深度解析 Skyscanner 的 SkyFloatingLabelTextField 项目

项目背景与痛点

在移动应用开发中,表单控件是用户输入数据的首要入口。而传统的文本框(TextField)往往在填写后才会展示输入的标签(Label),这无疑在用户交互上增加了一定的复杂性和遗忘感。Skyscanner 的 SkyFloatingLabelTextField 库正是为解决这一痛点应运而生,它实现了一种被称为 “浮标标签模式”(Float Label Pattern)的交互设计,巧妙地将标签随用户填写文本而上移,同时输入框显示文本内容。这样的设计不仅美观,还提升了用户体验,降低了用户出错率,尤其在移动设备上更能展现其优越性。

核心技术揭秘

SkyFloatingLabelTextField 的核心在于创建一个可以流畅变化并随着用户输入而位移的标签。这种控件依赖于 Swift 的强大能力和 iOS 平台的动态布局系统。这里展示一个简化的架构图来揭示其技术原理:

graph TD A{"激活 TextField 输入框"} -->|触发| B[用户输入文字] B --> C{"更新文本框内容并调整布局"} C --> D[Label 上移] C --> E[文本框展示内容] F[Label 层] -.-> G[TextField 层]

以下是它关键技术栈和设计模式的分析:

  1. 技术栈:该控件主要使用 Swift 编写,天然地可以利用 Swift 的现代、强大的语言特性,如可选项(Optionals)和闭包(Closures)来处理数据流和状态变化。
  2. 设计模式:它巧妙应用了观察者模式(Observer Pattern),通过 KVO(Key-Value Observing)监控用户的输入动作,根据输入包裹值的状态来触发界面变化,实现文本标签随文本输入而浮标上移的效果。
  3. 数据流管理:该控件对输入、布局和文本标签状态的管理,都展示了数据流的清晰分别与责任分割,通过监听输入不断更新界面元素的布局状态,确保了性能和用户体验的平衡。

功能亮点与差异

SkyFloatingLabelTextField 的亮点不仅体现在其独特的浮标标签行为上,更重要的是它高度可配置性和自定义性。如背景颜色、字体大小、文本和标签的颜色等,均可通过属性选项进行灵活调整,满足各种设计需求。在与其它同类控件的对比中,它尤为注重细节设计与用户体验,是开发团队经过众多迭代、打磨的结果。

应用场景与落地建议

使用该控件时,需要注意以下几点:

  • 考虑到动态布局计算对性能有一定影响,特别是在高频率频繁输入的情况下,应尽量减少不必要的监听和计算,以优化表现。
  • 对于输入框的样式和布局,请在设计阶段就进行细致推敲,确保控件能良好地融入应用的整体风格。

适合应用场景包括但不限于:

  • 登录/注册表单:文本框较小,需要明确提示每个输入字段的作用,同时保持简洁美观,浮标标签模式正合适。
  • 表单和配置页面:配置选项众多,用户需要频繁输入或切换配置值时,及时显示标签有助于记忆和理解,也提高了填写效率。

综合评价

SkyFloatingLabelTextField 提供了一种在 iOS 应用中优化文本输入体验的方法,它不仅美观且功能强大,非常适合追求用户体验的开发者群体。虽然在高频率输入情况下可能需注意性能监控,但总体上它的灵活性、可定制性以及用户体验的优化,使其在同类控件中表现卓越,值得在合适的移动项目中予以考虑和使用。

文档与社区

SkyFloatingLabelTextField 拥有详尽的开发文档和活跃的开源社区支持,有助于新手快速上手并方便社区中的高级开发者贡献和反馈。更多的具体实现细节和应用场景,可以前往该项目的文档进一步学习。

简要分析

热度分
5227
价值分
2628
活跃状态
归档

语言占比

Makefile
Objective-C
Ruby
Swift

Release

README

SkyFloatingLabelTextField

Build Status Coverage Status Pod Platform Pod License

Pod Version Carthage compatible Documentation Readme Score

SkyFloatingLabelTextField is a beautiful, flexible and customizable implementation of the space saving “Float Label Pattern”. This design enables adding context to input fields that are visible at the time of typing, while minimizing the additional space used to display this additional context. This component is used in the Skyscanner TravelPro iOS application in several places, like when searching for flights.

On top of implementing the space-saving floating title, the component also supports using iconography, RTL text support (e.g. Arabic & Hebrew), various states (error, selected, highlighted states), and is very much customizable and extensible.

Versioning

Up until version 1.2 Swift 2.2 and 2.3 compatible (and there is a Swift 2.3 branch in case you need it). From version 2.0 onwards only compatible with Swift 3. Please be mindful of the version you’re using.

Usage

To start using the component add it to your project using CocoaPods, Carthage or manually as per the Installation section.

The UI component can be used via the SkyFloatingLabelTextField class. To use icons on the right hand side, use the SkyFloatingLabelTextFieldWithIcon class. This control can be used very similar to UITextField - both from Interface Builder, or from code.

To create an instance of the class, use Interface builder, or do it from code. This example will create the following textbox with the placeholder and title:

let textField = SkyFloatingLabelTextField(frame: CGRect(x: 10, y: 10, width: 200, height: 45))
textField.placeholder = "Name"
textField.title = "Your full name"
self.view.addSubview(textField)

Colors

To customize the colors of the textfield, set a few properties - either from code, or from Interface builder. To use a textfield with an icon, utilize the SkyFloatingLabelTextFieldWithIcon class (and bundle the font class with your app). This example will change colors for the textfield on the right:

let lightGreyColor = UIColor(red: 197/255, green: 205/255, blue: 205/255, alpha: 1.0)
let darkGreyColor = UIColor(red: 52/255, green: 42/255, blue: 61/255, alpha: 1.0)
let overcastBlueColor = UIColor(red: 0, green: 187/255, blue: 204/255, alpha: 1.0)

let textField1 = SkyFloatingLabelTextField(frame: CGRect(x: 10, y: 10, width: 120, height: 45))
textField1.placeholder = "First name"
textField1.title = "Given name"
self.view.addSubview(textField1)

let textField2 = SkyFloatingLabelTextField(frame: CGRect(x: 150, y: 10, width: 120, height: 45))
textField2.placeholder = "Last name"
textField2.title = "Family name"

textField2.tintColor = overcastBlueColor // the color of the blinking cursor
textField2.textColor = darkGreyColor
textField2.lineColor = lightGreyColor
textField2.selectedTitleColor = overcastBlueColor
textField2.selectedLineColor = overcastBlueColor

textField2.lineHeight = 1.0 // bottom line height in points
textField2.selectedLineHeight = 2.0
self.view.addSubview(textField2)

Icons and fonts

Use the SkyFloatingLabelTextFieldWithIcon field to display icons next to the textfields. You have the option of using a font or an image as the icon by setting the iconType property (Default = IconType.font). If using an image as icon, you will have to set the iconImage property. If using a font as icon, you will have to set the iconFont property and bundle your icon with your app (if it’s not a built in one). Icons can be rotated and more precise positioning is also supported:

Using a font
let overcastBlueColor = UIColor(red: 0, green: 187/255, blue: 204/255, alpha: 1.0)
let textFieldFrame = CGRect(x: 150, y: 10, width: 120, height: 45)

let textField1 = SkyFloatingLabelTextFieldWithIcon(frame: textFieldFrame, iconType: .font)
textField1.placeholder = "Departure"
textField1.title = "Flying from"
textField1.iconFont = UIFont(name: "FontAwesome", size: 15)
textField1.iconText = "\u{f072}" // plane icon as per https://fortawesome.github.io/Font-Awesome/cheatsheet/
self.view.addSubview(textField1)

let textField2 = SkyFloatingLabelTextFieldWithIcon(frame: textFieldFrame)
textField2.placeholder = "Arrival"
textField2.title = "Flying to"
textField2.tintColor = overcastBlueColor
textField2.selectedTitleColor = overcastBlueColor
textField2.selectedLineColor = overcastBlueColor

// Set icon properties
textField2.iconType = .font
textField2.iconColor = UIColor.lightGrayColor()
textField2.selectedIconColor = overcastBlueColor
textField2.iconFont = UIFont(name: "FontAwesome", size: 15)
textField2.iconText = "\u{f072}" // plane icon as per https://fortawesome.github.io/Font-Awesome/cheatsheet/
textField2.iconMarginBottom = 4.0 // more precise icon positioning. Usually needed to tweak on a per font basis.
textField2.iconRotationDegrees = 90 // rotate it 90 degrees
textField2.iconMarginLeft = 2.0
self.view.addSubview(textField2)
Using an image
let textFieldframe = CGRect(x: 150, y: 10, width: 120, height: 45)

let textField1 = SkyFloatingLabelTextFieldWithIcon(frame: textFieldframe, iconType: .image)
textField1.placeholder = "Departure"
textField1.title = "Flying from"
textField1.iconImage = UIImage(imageLiteralResourceName: "PlaneIcon")
self.view.addSubview(textField1)

Error state and delegates

The textfield supports displaying an error state - this can be useful for example when validating fields on the fly. When the errorMessage property is set on the control, then the control is highlighted with the color set in the errorColor property.

To get notified of different events happening on the textfield - such as the text changing, editing starting or ending - just set the func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event) with the .editingChanged. also can set the delegate property to a class implementing the standard UITextFieldDelegate protocol:

class MyViewController: UIViewController, UITextFieldDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        let textField1 = SkyFloatingLabelTextField(frame: CGRect(x: 10, y: 10, width: 120, height: 45))
        textField1.placeholder = "Email"
        textField1.title = "Email address"
        textField1.errorColor = UIColor.redColor()
        textField1.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
        self.view.addSubview(textField1)
    }
    
    // This will notify us when something has changed on the textfield
    @objc func textFieldDidChange(_ textfield: UITextField) {
        if let text = textfield.text {
            if let floatingLabelTextField = textField as? SkyFloatingLabelTextField {
                if(text.characters.count < 3 || !text.containsString("@")) {
                    floatingLabelTextField.errorMessage = "Invalid email"
                }
                else {
                    // The error message will only disappear when we reset it to nil or empty string
                    floatingLabelTextField.errorMessage = ""
                }
            }
        }
    }
}

Disabled state

The textfield also supports displaying a disabled state. When the isEnabled property is set on the control, then the control is highlighted with the color set in the disabledColor property.

    textField.disabledColor = disabledColor
    textField.isEnabled = false

RTL language support

The component automatically detects the language writing direction. When the phone has a RTL language set (e.g. Arabic or Hebrew), then it automatically adjusts to support this style.

Alternatively, set the isLTRLanguage property to manually change the writing direction.

Further customizing the control by subclassing

The control was designed to allow further customization in subclasses. The control itself inherits from UITextField, so the standard overrides from there can all be used. A few other notable customization hooks via overriding are:

  • updateColors: override this method to customize colors whenever the state of the control changes
  • Layout overrides:
    • titleLabelRectForBounds(bounds:CGRect, editing:Bool): override to change the bounds of the top title placeholder view
    • textRectForBounds(bounds: CGRect): override to change the bounds of the control (inherited from UITextField)
    • editingRectForBounds(bounds: CGRect): override to change the bounds of the control when editing / selected (inherited from UITextField)
    • placeholderRectForBounds(bounds: CGRect): override to change the bounds of the placeholder view
    • lineViewRectForBounds(bounds:CGRect, editing:Bool): override to change the bounds of the bottom line view

Documentation

See the SkyFloatingLabelTextField documentation on CocoaDocs.org for the full documentation.

Installation

CocoaPods

The control is available through CocoaPods. CocoaPods can be installed using Ruby gems:

$ gem install cocoapods

Then simply add SkyFloatingLabelTextField to your Podfile:

pod 'SkyFloatingLabelTextField', '~> 3.0'

Lastly, let CocoaPods fetch the latest version of the component by running:

$ pod update
Integrating into Objective C projects

When integrating the component into an Objective C project, in the Podfile add use_frameworks!. For example as shown in SkyFloatingLabelTextFieldObjectiveCExample:

use_frameworks!

target 'SkyFloatingLabelTextFieldObjectiveCExample' do
  pod 'SkyFloatingLabelTextField', '~> 3.0'
end

Then to use the component in your code, add the following line to your .h or .m files:

@import SkyFloatingLabelTextField;
Carthage

The component supports Carthage. Start by making sure you have the latest version of Carthage installed. Using Homebrew run this:

$ brew update
$ brew install carthage

Then add SkyFloatingLabelTextField to your Cartfile:

github "Skyscanner/SkyFloatingLabelTextField"

Finally, add the framework to the Xcode project of your App. Link the framework to your App and copy it to the App’s Frameworks directory via the “Build Phases” section.

Manual

You can download the latest files from our Releases page. After doing so, copy the files in the Sources folder to your project.

Contributing

We welcome all contributions. Please read this guide before opening issues or submitting pull requests, as well as how and who to contact with questions.

Credits

The original component was built by Daniel Langh, Gergely Orosz and Raimon Laupente. Notable contributions by Shai Shamir (RTL language support).

Credits for the original design, and improving it with iconography to Matt D. Smith (@mds).

FAQ

  • Can I use it in Objective C projects?

    Of course! Please see the Integrating-into-Objective-C-projects section on how to integrate the component via CocoaPods.

  • Does the control work well with auto layout? What about using it programmatically?

    The control was built to support both use cases. It plays nicely with autolayout. As the control is a subclass of UITextField, overriding textRectForBounds(bounds:) or editingRectForBounds(bounds:) is always an option. Alternatively, overriding intrinsiccontentsize is also another possibility.

  • How can I remove the line from the bottom of the textfield?

    Set lineHeight and selectedLineHeight to 0, and the line won’t be displayed.

  • I’d like to validate the textfield using the errorMessage. How can I re-validate text is typed in the textfield?

    Using a delegate implement the textField(textField:,range:string:) method. This method fires whenever the text is changed - do the validation here. Alternatively, using subclassing you can also override the becomeFirstResponder method to e.g. clear the text or error message when the textfield is selected.

评论

暂无评论