This is the 13th day of my participation in the August More Text Challenge

In the process of App registration and login, there is often a process of verifying mobile phone verification code. To facilitate and clearly display the verification code entered by users, designers design the input process.

For example, here is the input UI for “get” the mobile phone verification code of the APP.

How about customizing the input field of the captcha?

When receiving user input, in addition to the usual UITextField and UITextView, we can also customize an input field. Word processing controls need to implement the UITextInput protocol, and UITextField and UITextView both implement UITextInput. When we implement the UITextInput protocol and draw the UI elements we need, a custom mobile phone verification code input box is implemented.

UITextInput agreement

UITextInput defines UITextInput

public protocol UITextInput : UIKeyInput {
    // omit specific content
}

public protocol UIKeyInput : UITextInputTraits {
    // omit specific content
}

public protocol UITextInputTraits : NSObjectProtocol {
    // omit specific content
}
Copy the code

Through the UITextInput protocol, you can obtain the user editing process, for example:

  • InsertText (_:) : Inserts a character
  • DeleteBackward () : Deletes characters
  • Replace (_:withText:) Replaces text

When the user’s input changes, the setNeedsDisplay() method is called to notify that the UI needs to be redrawn.

Custom UI

Customizing the UI can be done by overriding the draw(_:) method. In the draw(_:) method, you can draw UI elements such as borders, cursors, and characters.

For example, we want to implement a custom captcha input field called UnitField, defined as follows:

open class UnitField: UIControl {}Copy the code

Draw borders, characters, and other UI elements in the draw(_:) method:

open override func draw(_ rect: CGRect) {
    // Calculate the size of each captcha
    let width = (rect.size.width + CGFloat(unitSpace)) / CGFloat(inputUnitCount) - unitSpace
    let height = rect.size.height
    let unitSize = CGSize(width: width, height: height)

    // Get context information
    mCtx = UIGraphicsGetCurrentContext(a);// Draw the fill
    fill(rect: rect, unitSize: unitSize)
    // Draw the border
    drawBorder(rect: rect, unitSize: unitSize)
    // Draw text
    drawText(rect: rect, unitSize: unitSize)
    // Draw the trace box
    drawTrackBorder(rect: rect, unitSize: unitSize)
}
Copy the code

Where, when drawing characters, the position of each character is first calculated, and then the DRAW (in:withAttributes:) method of NSString is called to draw. The specific code is:

func drawText(rect: CGRect.unitSize: CGSize) {
    guard hasText else {
        return
    }
    
    let attr = [NSAttributedString.Key.foregroundColor: textColor,
                NSAttributedString.Key.font: textFont]

    for i in 0 ..< characters.count {
        let unitRect = CGRect(x: CGFloat(i) * (unitSize.width + unitSpace), y: 0, width: unitSize.width, height: unitSize.height)
        let yOffset = style = = .border ? 0 : borderWidth

        let subString = NSString(string: String(characters[i]))
        let oneTextSize = subString.size(withAttributes: attr)
        var drawRect = unitRect.insetBy(dx: (unitRect.size.width - oneTextSize.width) / 2,
                                        dy: (unitRect.size.height - oneTextSize.height) / 2)

        drawRect.size.height - = yOffset
        subString.draw(in: drawRect, withAttributes: attr)
    }
}
Copy the code

In addition, iOS 12 began to support automatic verification code filling. The UITextInputTraits protocol defines the textContentType: UITextContentType attribute, which is set to oneTimeCode to enable auto-populate.

Complete project

For a deeper look, check out UnitField’s complete project code, a custom captcha input box that works like UITextField.

It currently supports Cocopose and RxSwift.