iOS登录流程中如何复用相似但略有差异的视图?
复用登录流程中相似视图的实现方案
方案1:自定义通用输入视图,通过配置参数传递差异值
创建封装了输入框、标题标签等通用元素的自定义UIView,通过暴露配置属性适配邮箱/密码两种场景,把差异项转化为可配置参数。
示例代码:
import UIKit class AuthInputView: UIView { // 通用控件 private let titleLabel = UILabel() private let inputTextField = UITextField() // 配置属性,设置时自动更新UI var titleText: String? { didSet { titleLabel.text = titleText } } var inputPlaceholder: String? { didSet { inputTextField.placeholder = inputPlaceholder } } var keyboardType: UIKeyboardType = .default { didSet { inputTextField.keyboardType = keyboardType } } var isSecureInput: Bool = false { didSet { inputTextField.isSecureTextEntry = isSecureInput } } // 初始化方法 override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } // 构建通用UI布局 private func setupUI() { // 添加子控件并设置AutoLayout约束(示例简化,实际需根据布局需求实现) titleLabel.translatesAutoresizingMaskIntoConstraints = false inputTextField.translatesAutoresizingMaskIntoConstraints = false addSubview(titleLabel) addSubview(inputTextField) NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: topAnchor), titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor), inputTextField.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8), inputTextField.leadingAnchor.constraint(equalTo: leadingAnchor), inputTextField.trailingAnchor.constraint(equalTo: trailingAnchor), inputTextField.heightAnchor.constraint(equalToConstant: 44) ]) titleLabel.font = .systemFont(ofSize: 16, weight: .medium) inputTextField.borderStyle = .roundedRect } }
在ViewController中使用时传入不同配置:
// 邮箱页面 class EmailViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let emailInputView = AuthInputView(frame: CGRect(x: 20, y: 100, width: view.bounds.width - 40, height: 80)) emailInputView.titleText = "Enter email" emailInputView.inputPlaceholder = "your@email.com" emailInputView.keyboardType = .emailAddress view.addSubview(emailInputView) } } // 密码页面 class PasswordViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let passwordInputView = AuthInputView(frame: CGRect(x: 20, y: 100, width: view.bounds.width - 40, height: 80)) passwordInputView.titleText = "Enter password" passwordInputView.inputPlaceholder = "your password" passwordInputView.keyboardType = .asciiCapable passwordInputView.isSecureInput = true view.addSubview(passwordInputView) } }
方案2:用同一个ViewController,通过类型枚举区分场景
如果两个页面逻辑高度重合,可只用一个通用ViewController,通过枚举标记当前场景,内部根据枚举值配置UI差异。
示例代码:
import UIKit enum AuthScene { case email case password } class AuthViewController: UIViewController { private let titleLabel = UILabel() private let inputTextField = UITextField() private let currentScene: AuthScene // 初始化时传入场景类型 init(scene: AuthScene) { self.currentScene = scene super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() setupCommonUI() configureForScene() } private func setupCommonUI() { // 通用布局配置 titleLabel.translatesAutoresizingMaskIntoConstraints = false inputTextField.translatesAutoresizingMaskIntoConstraints = false view.addSubview(titleLabel) view.addSubview(inputTextField) NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 100), titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), inputTextField.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8), inputTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), inputTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), inputTextField.heightAnchor.constraint(equalToConstant: 44) ]) titleLabel.font = .systemFont(ofSize: 16, weight: .medium) inputTextField.borderStyle = .roundedRect } // 根据场景配置差异项 private func configureForScene() { switch currentScene { case .email: titleLabel.text = "Enter email" inputTextField.placeholder = "your@email.com" inputTextField.keyboardType = .emailAddress case .password: titleLabel.text = "Enter password" inputTextField.placeholder = "your password" inputTextField.keyboardType = .asciiCapable inputTextField.isSecureTextEntry = true } } // 通用提交逻辑 @objc private func submitTapped() { guard let inputText = inputTextField.text, !inputText.isEmpty else { return } if currentScene == .email { // 邮箱验证通过后跳转密码页面 let passwordVC = AuthViewController(scene: .password) present(passwordVC, animated: true) } else { // 密码验证逻辑 } } }
使用时直接创建对应场景的实例:
// 启动登录流程 let emailVC = AuthViewController(scene: .email) present(emailVC, animated: true)
方案3:基于基类ViewController的继承复用
如果需要保留两个独立的ViewController但减少重复代码,可先写基类封装通用逻辑,子类仅实现差异部分。
示例代码:
import UIKit class BaseAuthViewController: UIViewController { // 通用控件 let titleLabel = UILabel() let inputTextField = UITextField() override func viewDidLoad() { super.viewDidLoad() setupCommonUI() configureUI() // 子类重写此方法配置差异 } // 通用UI布局和逻辑 private func setupCommonUI() { titleLabel.translatesAutoresizingMaskIntoConstraints = false inputTextField.translatesAutoresizingMaskIntoConstraints = false view.addSubview(titleLabel) view.addSubview(inputTextField) NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 100), titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), inputTextField.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8), inputTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), inputTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), inputTextField.heightAnchor.constraint(equalToConstant: 44) ]) titleLabel.font = .systemFont(ofSize: 16, weight: .medium) inputTextField.borderStyle = .roundedRect } // 子类必须重写,配置差异项 func configureUI() {} } // 邮箱子类 class EmailViewController: BaseAuthViewController { override func configureUI() { titleLabel.text = "Enter email" inputTextField.placeholder = "your@email.com" inputTextField.keyboardType = .emailAddress } // 邮箱专属逻辑可在此补充 } // 密码子类 class PasswordViewController: BaseAuthViewController { override func configureUI() { titleLabel.text = "Enter password" inputTextField.placeholder = "your password" inputTextField.keyboardType = .asciiCapable inputTextField.isSecureTextEntry = true } // 密码专属逻辑可在此补充 }
三个方案可按需选择:仅视图重复选方案1;页面逻辑高度一致选方案2;需保留独立ViewController选方案3。
内容的提问来源于stack exchange,提问作者Nala
相关产品推荐
相关产品推荐

