UIStackView 中基于条件翻转视图的问题

问题描述 投票:0回答:1

在我的 iOS 项目中,我正在使用包含多个子视图的 UIStackView。我想根据条件动态翻转或上下移动堆栈视图中的特定视图。具体来说,

我正在尝试操纵名为“FIRST”和“SECOND”的两个文本字段的位置。但是,我遇到了一个问题,即翻转仅在每秒点击或单击时才能正确工作。

这是我的代码

import UIKit
import Combine

import SnapKit

class ViewController: UIViewController {
    
    private var toggleSubject = PassthroughSubject<Bool, Never>()
    private var toggle = false
    private var store = Set<AnyCancellable>()
    
    private lazy var topField: AppTextField = {
        let view = AppTextField(title: "First")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var bottomField: AppTextField = {
        let view = AppTextField(title: "Second")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var lineView: UIImageView = {
        let view = UIImageView(frame: .zero)
        view.backgroundColor = .red
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var stackView: UIStackView = {
        let view = UIStackView(arrangedSubviews: [topField, lineView, bottomField, toggleButton])
        view.axis  = .vertical
        view.spacing = 4.0
        view.distribution = .fill
        view.alignment = .fill
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var toggleButton: UIButton = {
        let view = UIButton(frame: .zero)
        view.setTitle("Toggle", for: .normal)
        view.setTitleColor(.white, for: .normal)
        view.setTitleColor(.systemYellow, for: .highlighted)
        view.backgroundColor = .darkGray
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    
    private lazy var selectedTopFieldLabel: UILabel = {
        let view = UILabel(frame: .zero)
        view.textAlignment = .center
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(stackView)
        self.view.addSubview(selectedTopFieldLabel)
        toggleSubject.send(toggle)
        
        stackView.snp.makeConstraints { make in
            make.leading.trailing.centerY.equalToSuperview().inset(16)
        }
        
        lineView.snp.makeConstraints { make in
            make.height.equalTo(0.5)
        }
        
        selectedTopFieldLabel.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(200)
        }
        
        
        toggleButton.addTarget(self, action: #selector(toggleButtonAction), for: .touchUpInside)
        
        toggleSubject.sink { toggle in
            print("---> Changed Toggle",toggle)
            self.updateUI(toggle: toggle)
            UIView.animate(withDuration: 0.25) {
                self.stackView.layoutIfNeeded()
            }
        }.store(in: &store)
        self.updateUI(toggle: toggle)
    }
    
    @objc func toggleButtonAction() {
        toggle.toggle()
        toggleSubject.send(toggle)
    }
    
    private func updateUI(toggle: Bool) {
        let arrangedSubviews = self.stackView.arrangedSubviews
        
        guard var first = arrangedSubviews[0] as? AppTextField,
              var second = arrangedSubviews[2] as? AppTextField else {return}
        stackView.arrangedSubviews.forEach({$0.removeFromSuperview()})
        let changedViews = toggle ? [first, lineView, second, toggleButton] : [second, lineView, first , toggleButton]

        guard let firstTextField = changedViews.first as? AppTextField,
              let firstfieldPlaceholder = firstTextField.placeholder else {return}

        self.selectedTopFieldLabel.text =  "Selected On Top " + " -> " + firstfieldPlaceholder

        changedViews.forEach { view in
            self.stackView.addArrangedSubview(view)
        }
        // Update constraints
        UIView.animate(withDuration: 0.25) {
            self.stackView.setNeedsLayout()
            self.stackView.layoutIfNeeded()
        }
    }
}

class AppTextField: UIView {
    
    private lazy var inputField: UITextField = {
        let textField = UITextField(frame: .zero)
        textField.backgroundColor = .lightGray.withAlphaComponent(0.15)
        textField.translatesAutoresizingMaskIntoConstraints = false
        return textField
    }()
    
    
    var title: String? {
        get { return inputField.placeholder }
        set { inputField.placeholder = newValue }
    }
    
    var placeholder: String? {
        get { return inputField.placeholder }
        set { inputField.placeholder = newValue }
    }
    
    var text: String? {
        get { return inputField.text }
        set { inputField.text = newValue }
    }
    
    init(title: String) {
        super.init(frame: .zero)
        self.title = title
        setupViews()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupViews() {
        addSubview(inputField)
        inputField.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            make.height.equalTo(48)
        }
    }
}

提前致谢。

ios swift uistackview
1个回答
0
投票

我可以将上面的代码更改为

  1. 使用
    CurrentValueSubject<Bool, Never>(false)
    来避免额外的
    Bool
    变量
  2. StackView 可以
    addArrangedSubview
    无需删除子视图然后重新添加

切换动作应该是:

private var toggleSubject = CurrentValueSubject<Bool, Never>(false)
@objc func toggleButtonAction() {
    toggleSubject.value.toggle()
}

...
//Keep toggleSubject sink as it was
toggleSubject
    .sink { [weak self] toggle in
        self?.updateUI(toggle: toggle)
    }
    .store(in: &store)

然后删除

updateUI
中的其他内容:

private func updateUI(toggle: Bool) {
    if toggle {
        self.stackView.addArrangedSubview(topField)
        self.stackView.addArrangedSubview(lineView)
        self.stackView.addArrangedSubview(bottomField)
        self.stackView.addArrangedSubview(toggleButton)
    } else {
        self.stackView.addArrangedSubview(bottomField)
        self.stackView.addArrangedSubview(lineView)
        self.stackView.addArrangedSubview(topField)
        self.stackView.addArrangedSubview(toggleButton)
    }
    UIView.animate(withDuration: 0.25) {
        self.stackView.setNeedsLayout()
        self.stackView.layoutIfNeeded()
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.