【问题标题】:How to remove and addArrangedSubvew without view blinking?如何在视图不闪烁的情况下删除和添加 ArrangedSubview?
【发布时间】:2021-05-28 17:47:22
【问题描述】:

我有一些来自一个堆栈视图的视图。每隔n 秒,我应该使用来自服务器的数据更新此视图。当新数据到来时,我清理stackView(使用其子项的removeFromSuperView 方法)并再次添加arrangedSubviews 以更新UI。有时,服务器会发送与旧数据相同的数据。但是做这个更新操作,我的看法有点不寒而栗。每次我清理并向我的堆栈视图添加视图时,它有点拖拉和颤抖。当然,我只能在 oldData != newData 时更新我的​​ UI。但这场比赛很难,也很难正确找到。那么,如何在不颤抖和眨眼的情况下用新数据更新stackview?

这是我的代码:

func configure(_ items: [Item]) {
    stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
    items.forEach { 
       let someView = SomeView()
       someView.configure($0)
       stackView.addArrangedSubview(someView)
    }
}

【问题讨论】:

  • 为什么要重新创建视图?为什么你不能只创建一次并保持对这些视图的引用并每隔 n 秒更新一次,这样视图将在不闪烁的情况下更新
  • 假设,我持有引用并将它们存储为像 [SomeView] 这样的数组。但是下一个更新数据的长度大于 [SomeView] 长度。如你所见,无论如何,我应该重新创建视图。
  • 为什么不用UICollectionView(或者UITableView)呢?这些旨在(取消)队列视图
  • 目前不可能。因为所有的项目都在卡片内,就像视图一样。是否可以将 tableview 放在像 view 一样的卡片中并使卡片的高度适合 tableview?我目前不知道。
  • @neo - 什么是SomeView?您不必为了更新它们而删除和添加所有排列的子视图。如果您向我们展示 SomeView 是什么(以及 Item 是什么),我可以向您展示一个更好的方法。

标签: ios swift uiview uistackview


【解决方案1】:

而不是将视图删除/重新添加到堆栈视图...

  • 仅当 Item 多于当前排列的子视图时才添加新视图
  • 设置数据 - configure() - 每个排列的子视图
  • 如果有多余的子视图,则隐藏现有的排列子视图

例如:

func configure(_ items: [Item]) {
    // if we have fewer arranged subviews than items
    //  add new ones
    while stackView.arrangedSubviews.count < items.count {
        stackView.addArrangedSubview(SomeView())
    }
    // hide any existing arranged subviews if we have too many
    for i in 0..<stackView.arrangedSubviews.count {
        stackView.arrangedSubviews[i].isHidden = i >= items.count
    }
    // update the existing arranged subviews with the new data
    for (thisItem, thisView) in zip(items, stackView.arrangedSubviews) {
        // unwrap the arranged subview
        guard let v = thisView as? SomeView else {
            continue
        }
        v.configure(thisItem)
    }
}

这是一个完整的示例 - “项目”的数量及其数据将每 1.5 秒更改一次:

带有两个字符串的示例结构项目:

struct Item {
    var title: String = ""
    var desc: String = ""
}

带有两个标签的示例“SomeView”类:

class SomeView: UIView {
    let titleLabel = UILabel()
    let descLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        backgroundColor = .systemYellow
        titleLabel.font = .boldSystemFont(ofSize: 18.0)
        descLabel.font = .italicSystemFont(ofSize: 15.0)
        [titleLabel, descLabel].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            v.backgroundColor = .white // so we can see the label frames at run-time
            addSubview(v)
        }
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
            titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
            titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),

            descLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4.0),

            descLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
            descLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
            descLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
        ])
    }
    
    func configure(_ item: Item) -> Void {
        titleLabel.text = item.title
        descLabel.text = item.desc
    }
}

带有堆栈视图的示例“NeoView”类:

class NeoView: UIView {

    let stackView: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.spacing = 8
        return v
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        stackView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
            stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
            stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
        ])
    }
    
    func configure(_ items: [Item]) {
        // if we have fewer arranged subviews than items
        //  add new ones
        while stackView.arrangedSubviews.count < items.count {
            stackView.addArrangedSubview(SomeView())
        }
        // hide any existing arranged subviews if we have too many
        for i in 0..<stackView.arrangedSubviews.count {
            stackView.arrangedSubviews[i].isHidden = i >= items.count
        }
        // update the existing arranged subviews with the new data
        for (thisItem, thisView) in zip(items, stackView.arrangedSubviews) {
            // unwrap the arranged subview
            guard let v = thisView as? SomeView else {
                continue
            }
            v.configure(thisItem)
        }
    }
}

示例控制器类:

class NeoTestViewController: UIViewController {
    
    let neoView: NeoView = {
        let v = NeoView()
        v.backgroundColor = .systemTeal
        return v
    }()
    
    var items: [[Item]] = []
    var idx: Int = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        neoView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(neoView)
        
        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            neoView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            neoView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            neoView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            // no bottom constraint
        ])
        
        items = sampleData()
        
        var idx: Int = 0
        self.neoView.configure(self.items[idx % self.items.count])

        // update neoView with a new set of items every 1.5 seconds
        Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { timer in
            idx += 1
            self.neoView.configure(self.items[idx % self.items.count])
        }
        
    }
    
    func sampleData() -> [[Item]] {

        // we'll create 8 sets of [Item] arrays
        //  each with a different number of Items
        let numItems: [Int] = [
            4, 2, 6, 3, 7, 8, 3, 5,
        ]
        
        var items: [[Item]] = []
        var i: Int = 1
        numItems.forEach { n in
            var theseItems: [Item] = []
            for j in 1...n {
                let thisItem = Item(title: "Set \(i) item \(j) title.", desc: "Set \(i) item \(j) description.")
                theseItems.append(thisItem)
            }
            theseItems[0].title = "Set \(i) has \(n) items."
            items.append(theseItems)
            i += 1
        }

        return items
    }
    
}

【讨论】:

    猜你喜欢
    • 2019-11-12
    • 1970-01-01
    • 2023-03-13
    • 2013-01-02
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 2011-01-16
    • 2013-07-12
    相关资源
    最近更新 更多