Visual_Novel_iOS/crush/Crush/Src/Components/UI/TextViews/CLTextField.swift

352 lines
11 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// CLTextField.swift
// Crush
//
// Created by Leon on 2025/7/15.
//
import UIKit
class CLTextField: UITextField {
// MARK: - Properties
private var focusBorderColor: UIColor = .orange
private var errorBorderColor: UIColor = .red
private var nowBorderColor: UIColor = .clear
private var tapTopButtonAction: (() -> Void)?
/// errorBorder border
private var errorBorderShowMode: Bool = false
private var alwaysShowRightViewConfig: Bool = false
private var alwaysShowClearButtonIfHave: Bool = false
private var disableAllBorder: Bool = false
private var fakeTextFieldForInputTrigger: Bool = false
var cornerRadiusConfig:CGFloat = 8
// private var defaultTxt: String?
// Closures for delegate-like events (assumed from Objective-C)
var textDidChanged: ((CLTextField) -> Void)?
var textDidBeginEditing: ((CLTextField) -> Void)?
var textDidEndEditing: ((CLTextField) -> Void)?
var textShouldBeginEditing: ((CLTextField) -> Bool)?
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
baseCommonSetup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
baseCommonSetup()
}
private func baseCommonSetup() {
// keyboardAppearance = .dark
// reloadInputViews()
setContentHuggingPriority(UILayoutPriority(244), for: .horizontal)
let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
leftView.backgroundColor = .clear
self.leftViewMode = .always
self.leftView = leftView
// --- 3 events
addTarget(self, action: #selector(textDidChanged(_:)), for: .editingChanged)
NotificationCenter.default.addObserver(self, selector: #selector(notiTextDidBeginEditing), name: UITextField.textDidBeginEditingNotification, object: self)
NotificationCenter.default.addObserver(self, selector: #selector(notiTextDidEndEditing), name: UITextField.textDidEndEditingNotification, object: self)
NotificationCenter.default.addObserver(self, selector: #selector(notiTextShouldBeginEditing(_:)), name: UITextField.textDidBeginEditingNotification, object: self)
setupStandard()
}
// MARK: - Public Methods
func setupStandard() {
autocorrectionType = .no
backgroundColor = .c.csen
font = CLSystemToken.font(token: .tbl)
textColor = .white
focusBorderColor = .theme
errorBorderColor = .red
layer.borderWidth = 1
layer.borderColor = UIColor.clear.cgColor
layer.cornerRadius = cornerRadiusConfig
// NSLayoutConstraint.activate([
// heightAnchor.constraint(equalToConstant: 48)
// ])
snp.makeConstraints { make in
make.height.equalTo(48)
}
// default
setupRightClearButton()
}
func setupPlaceholder(_ placeholder: String) {
let font = UIFont.systemFont(ofSize: 15)
let color = UIColor.gray
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: color
]
attributedPlaceholder = NSAttributedString(string: placeholder, attributes: attributes)
}
func setupTopButton(with action: (() -> Void)?) {
let topButton = UIButton(type: .custom)
topButton.backgroundColor = .clear
addSubview(topButton)
topButton.addTarget(self, action: #selector(tapTopButton), for: .touchUpInside)
tapTopButtonAction = action
// Using SnapKit or native Auto Layout
topButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topButton.topAnchor.constraint(equalTo: topAnchor),
topButton.bottomAnchor.constraint(equalTo: bottomAnchor),
topButton.leadingAnchor.constraint(equalTo: leadingAnchor),
topButton.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
func showAlwayErrorBorder() {
errorBorderShowMode = true
layer.borderWidth = 1
layer.borderColor = errorBorderColor.cgColor
}
func hideErrorBorder() {
errorBorderShowMode = false
layer.borderColor = UIColor.clear.cgColor
}
func sendTextChangedNoti(){
NotificationCenter.default.post(
name: UITextField.textDidChangeNotification,
object: self)
}
// MARK: - Helper Methods
func setupRightClearButton() {
rightViewMode = .whileEditing
let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
self.rightView = rightView
let clearButton = EPIconGhostSecondaryButton(radius: .none, iconSize: .small, iconCode: .delete02)
rightView.addSubview(clearButton)
clearButton.addTarget(self, action: #selector(tapClearButton), for: .touchUpInside)
clearButton.snp.makeConstraints { make in
make.size.equalTo(clearButton.bgImageSize())
make.centerY.equalToSuperview()
make.trailing.equalToSuperview().offset(-8)
}
}
func setupRightEyeButton() {
isSecureTextEntry = true
rightViewMode = .always
alwaysShowRightViewConfig = true
let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
self.rightView = rightView
}
func setupRightRightArrowButton() -> UIButton? {
rightViewMode = .always
alwaysShowRightViewConfig = true
let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
self.rightView = rightView
return nil
}
func setupRightViewEmpty() {
rightView = nil
}
func setupLeftCoinIconView() {
leftViewMode = .always
let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 40))
let iconView = UIImageView(frame: CGRect(x: 16, y: 12, width: 16, height: 16))
iconView.image = UIImage(named: "icon_16_diamond")
leftView.addSubview(iconView)
self.leftView = leftView
}
func setupLeftBlankView() {
leftViewMode = .always
let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 40))
self.leftView = leftView
}
func adjustRightViewMode() {
if alwaysShowRightViewConfig {
rightViewMode = .always
return
}
//
if text?.isEmpty ?? true {
rightViewMode = .never
} else {
rightViewMode = alwaysShowClearButtonIfHave ? .always : .whileEditing
}
}
// MARK: - Actions
@objc private func tapTopButton() {
tapTopButtonAction?()
}
@objc private func tapClearButton() {
text = ""
textDidChanged(self)
NotificationCenter.default.post(
name: UITextField.textDidChangeNotification,
object: self)
}
@objc private func tapRightArrowButton(_ button: UIButton) {
// Empty implementation as in original
}
// MARK: - Textfield Events
@objc private func textDidChanged(_ textField: UITextField) {
errorBorderShowMode = false
nowBorderColor = focusBorderColor
if !disableAllBorder && textField.isFirstResponder {
layer.borderColor = nowBorderColor.cgColor
}
adjustRightViewMode()
textDidChanged?(self)
}
@objc private func notiTextDidBeginEditing() {
if !errorBorderShowMode && !disableAllBorder {
layer.borderColor = focusBorderColor.cgColor
layer.borderWidth = 1
}
textDidBeginEditing?(self)
adjustRightViewMode()
}
@objc private func notiTextDidEndEditing() {
if !errorBorderShowMode {
layer.borderColor = nil
layer.borderWidth = 0
}
textDidEndEditing?(self)
}
@objc private func notiTextShouldBeginEditing(_ notification: Notification) {
if fakeTextFieldForInputTrigger {
resignFirstResponder()
}
textShouldBeginEditing?(self)
}
// MARK: - Setters
override var delegate: UITextFieldDelegate? {
get { super.delegate }
set {
if newValue !== self {
// print("block") // Uncomment if assertion is needed
}
super.delegate = newValue
}
}
override var placeholder: String? {
didSet {
if let placeholder = placeholder {
setupPlaceholder(placeholder)
}
}
}
// var defaultTxt: String? {
// get { _defaultTxt }
// set {
// _defaultTxt = newValue
// super.text = newValue
// }
// }
override var text: String? {
didSet {
if text != oldValue {
textDidChanged(self)
}
}
}
// MARK: - Layout
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
var rightViewRect = super.rightViewRect(forBounds: bounds)
if #available(iOS 13.0, *) {
// fix iOS 13 rightView,
let left = frame.size.width - (rightView?.frame.size.width ?? 0)
let top = round((frame.size.height - (rightView?.frame.size.height ?? 0)) / 2.0)
rightViewRect = CGRect(x: left, y: top, width: rightView?.frame.size.width ?? 0, height: rightView?.frame.size.height ?? 0)
}
return rightViewRect
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = self.cornerRadiusConfig
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
extension CLTextField{
func switchToNaviSearchField(){
setupPlaceholder("Search")
font = .t.tbm
cornerRadiusConfig = 16// height: 32
snp.updateConstraints { make in
make.height.equalTo(32)
}
leftViewMode = .always
let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 32))
leftView.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 40, height: 32))
}
self.leftView = leftView
let image = MWIconFont.image(fromIcon: .search, size: CGSize(width: 12, height: 12), color: .white)
let icon = UIImageView(image: image)
leftView.addSubview(icon)
icon.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 12, height: 12))
make.centerY.equalToSuperview()
make.leading.equalToSuperview().offset(12)
}
setNeedsLayout()
layoutIfNeeded()
}
}