mirror of
https://github.com/confirmedcode/Lockdown-iOS.git
synced 2025-12-21 12:14:02 +01:00
171 lines
5.2 KiB
Swift
171 lines
5.2 KiB
Swift
//
|
|
// TextBox.swift
|
|
// Lockdown
|
|
//
|
|
// Created by Alexander Parshakov on 11/3/22
|
|
// Copyright © 2022 Confirmed Inc. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
internal final class TextBox: UIView {
|
|
|
|
private let leftTextMargin: CGFloat = 16
|
|
|
|
private(set) var state = TextInputState.placeholder
|
|
|
|
var title: String? {
|
|
didSet {
|
|
titleLabel.text = title
|
|
titlePlaceholderLabel.text = title
|
|
}
|
|
}
|
|
|
|
var titleColor: UIColor? {
|
|
didSet {
|
|
titleLabel.textColor = titleColor
|
|
titlePlaceholderLabel.textColor = titleColor
|
|
}
|
|
}
|
|
|
|
var placeholderFont: UIFont? = .regularLockdownFont(size: 17) {
|
|
didSet {
|
|
titlePlaceholderLabel.font = placeholderFont
|
|
placeholderLabel.font = placeholderFont
|
|
}
|
|
}
|
|
|
|
let titleLabel: UILabel = TextBoxLabel(fontSize: UIFont.smallSystemFontSize)
|
|
let titlePlaceholderLabel: UILabel = TextBoxLabel()
|
|
let placeholderLabel: UILabel = TextBoxLabel()
|
|
|
|
private let titleBottomSpace: CGFloat = 2
|
|
private let placeholderBottom: CGFloat = 6
|
|
|
|
// MARK: - Init
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
commonInit()
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
super.init(coder: aDecoder)
|
|
commonInit()
|
|
}
|
|
|
|
// MARK: - Internal
|
|
|
|
var editingTextInsets: UIEdgeInsets {
|
|
return UIEdgeInsets(
|
|
top: titleLabel.font.lineHeight + titleBottomSpace,
|
|
left: leftTextMargin,
|
|
bottom: 0,
|
|
right: leftTextMargin)
|
|
}
|
|
|
|
func setState(_ newState: TextInputState, animated: Bool) {
|
|
let oldSate = state
|
|
state = newState
|
|
let isAnimated = animated && window != nil && frame != .zero
|
|
|
|
switch (oldSate, newState, isAnimated) {
|
|
case (_, .empty, true):
|
|
moveTitleDown()
|
|
case (.empty, .placeholder, true):
|
|
moveTitleUp()
|
|
default:
|
|
stateDidUpdate()
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func commonInit() {
|
|
isUserInteractionEnabled = false
|
|
let subviews = [
|
|
titleLabel,
|
|
titlePlaceholderLabel,
|
|
placeholderLabel
|
|
]
|
|
for subview in subviews {
|
|
subview.translatesAutoresizingMaskIntoConstraints = false
|
|
subview.isUserInteractionEnabled = false
|
|
addSubview(subview)
|
|
}
|
|
setupConstraints()
|
|
// debug()
|
|
}
|
|
|
|
private func setupConstraints() {
|
|
layoutMargins = .zero
|
|
|
|
NSLayoutConstraint.activate([
|
|
titleLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 8),
|
|
titleLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: leftTextMargin),
|
|
titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.trailingAnchor),
|
|
|
|
titlePlaceholderLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
|
|
titlePlaceholderLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: leftTextMargin),
|
|
titlePlaceholderLabel.trailingAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.trailingAnchor),
|
|
|
|
placeholderLabel.topAnchor.constraint(equalTo: titlePlaceholderLabel.topAnchor),
|
|
placeholderLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
|
|
placeholderLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor)
|
|
])
|
|
}
|
|
|
|
private func stateDidUpdate() {
|
|
updateTitle()
|
|
updatePlaceholder()
|
|
}
|
|
|
|
private func updateTitle() {
|
|
switch state {
|
|
case .empty:
|
|
titleLabel.isHidden = true
|
|
titlePlaceholderLabel.isHidden = false
|
|
case .text, .placeholder, .textInput:
|
|
titleLabel.isHidden = false
|
|
titlePlaceholderLabel.isHidden = true
|
|
}
|
|
}
|
|
|
|
private func updatePlaceholder() {
|
|
placeholderLabel.alpha = (state == .placeholder) ? 1 : 0
|
|
}
|
|
|
|
private func moveTitleDown() {
|
|
titlePlaceholderLabel.transform = transform(
|
|
from: titleLabel.frame,
|
|
to: titlePlaceholderLabel.frame)
|
|
animateTitles()
|
|
}
|
|
|
|
private func moveTitleUp() {
|
|
titleLabel.transform = transform(
|
|
from: titlePlaceholderLabel.frame,
|
|
to: titleLabel.frame)
|
|
animateTitles()
|
|
}
|
|
|
|
private func animateTitles() {
|
|
updateTitle()
|
|
UIView.animate(withDuration: 0.25) {
|
|
self.titleLabel.transform = .identity
|
|
self.titlePlaceholderLabel.transform = .identity
|
|
self.updatePlaceholder()
|
|
}
|
|
}
|
|
|
|
private func transform(from source: CGRect, to destination: CGRect) -> CGAffineTransform {
|
|
let scaleX = source.width / destination.width
|
|
let scaleY = source.height / destination.height
|
|
|
|
let translationX = source.origin.x - destination.origin.x - (destination.width * (1.0 - scaleX) / 2)
|
|
let translationY = source.origin.y - destination.origin.y - (destination.height * (1.0 - scaleY) / 2)
|
|
|
|
return CGAffineTransform(translationX: translationX, y: translationY).scaledBy(x: scaleX, y: scaleY)
|
|
}
|
|
}
|