環境
- iOS14.5
サブクラスを作成
import UIKit
final class UnitTextField: UITextField {
...
}
UITextFieldのサブクラスを作成します。
各種、オーバライドメソッドの作成
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
}
プロパティの作成
var suffix: String?
suffixを使って、RightViewの文言を設定していきます。
RightViewにLabelをセットする
extension UnitTextField {
func commonInit() {
let rightLabel = UILabel(frame: .zero)
rightLabel.font = font
rightLabel.text = suffix
rightLabel.textColor = UIColor.black
rightLabel.sizeToFit()
let padding: CGFloat = 8
let container = UIView(frame: CGRect(x: 0, y: 0, width: rightLabel.bounds.width + padding, height: rightLabel.bounds.height))
container.addSubview(rightLabel)
rightLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
rightLabel.topAnchor.constraint(equalTo: container.topAnchor),
rightLabel.leftAnchor.constraint(equalTo: container.leftAnchor),
rightLabel.bottomAnchor.constraint(equalTo: container.bottomAnchor),
rightLabel.rightAnchor.constraint(equalTo: container.rightAnchor, constant: -padding),
])
rightViewMode = .always
rightView = container
}
}
ContainerをPadding分広げます。
commonInitの呼び出し
var suffix: String? {
didSet {
commonInit()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
}
suffixにdidSetを使ってcommonInit()を呼び出すことによって、動的に単位を変えることがあっても対応できるようになります。
サブクラスを適用
@IBOutlet weak var textField: UnitTextField!
StoryboardのCustomClassとコードを書き換えます。
UnitTextの呼び出し
textField.suffix = "キロメートル"
rightViewにラベルを表示したい時はsuffixに値を入れることにより実現します。
これで完成です。

全体
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textField: UnitTextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.suffix = "キロメートル"
}
}
final class UnitTextField: UITextField {
var suffix: String? {
didSet {
commonInit()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
}
}
extension UnitTextField {
func commonInit() {
let rightLabel = UILabel(frame: .zero)
rightLabel.font = font
rightLabel.text = suffix
rightLabel.textColor = UIColor.black
rightLabel.sizeToFit()
let padding: CGFloat = 8
let container = UIView(frame: CGRect(x: 0, y: 0, width: rightLabel.bounds.width + padding, height: rightLabel.bounds.height))
container.addSubview(rightLabel)
rightLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
rightLabel.topAnchor.constraint(equalTo: container.topAnchor),
rightLabel.leftAnchor.constraint(equalTo: container.leftAnchor),
rightLabel.bottomAnchor.constraint(equalTo: container.bottomAnchor),
rightLabel.rightAnchor.constraint(equalTo: container.rightAnchor, constant: -padding),
])
rightViewMode = .always
rightView = container
}
}
動作確認

このように動的に単位を変えてもレイアウトが崩れることはないようです。
失敗例
rightViewRect とeditingRect をつかってインセットを調整しようとしたのですが、これは余白は上手くいくのですが、カーソルの位置がTextField外にズレるなどあり失敗しました。
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
var rect = super.rightViewRect(forBounds: bounds)
// RightViewの右側に11ポイント余白を付ける
rect.origin.x -= 11
return rect
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
var rect = super.editingRect(forBounds: bounds)
rect.origin.x -= 11
return rect
}

参考
- https://developer.apple.com/documentation/uikit/uitextfield/1619638-rightviewrect
- https://stackoverflow.com/questions/42082339/add-a-button-on-right-view-of-uitextfield-in-such-way-that-text-should-not-over
- https://iganin.hatenablog.com/entry/2019/06/20/003754
- https://medium.com/@heitara/ios-13-uitextfiled-changes-25bd6238e7d3
- https://stackoverflow.com/questions/19371018/uitextfield-leftview-rightview-padding-on-ios7