You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS动态UIScrollView实现:动态增减子视图并调整高度

解决UIScrollView动态高度及视图添加问题

Hey there! Let's work through this UIScrollView dynamic update issue step by step— I totally get what you're aiming for: tapping the plus button on a view adds a new view below it, and this can repeat infinitely, with the scrollview adjusting to fit all the new content. Here's how to make it happen:

1. Lay a Solid Foundation for the ScrollView

First, you'll want a container view inside your UIScrollView (let's call it contentView). All your dynamically added views will live here— this makes calculating and updating the total height way easier. Set up these constraints:

  • Pin contentView's top, bottom, leading, and trailing edges to the UIScrollView's inner margins
  • Match contentView's width to the UIScrollView's width (to avoid unwanted horizontal scrolling)
  • Pin the UIScrollView itself to its parent view's layout guides (like safe area) as needed

With this setup, the UIScrollView will automatically adjust its contentSize based on the contentView's height— no manual size calculations required later.

2. Build a Reusable View Component

Create a custom view (e.g., AddableItemView) that includes the plus button and any other content you need. Give it a fixed height (or let it auto-size based on internal content) so we know how much space each new view will take.

Example Swift code:

class AddableItemView: UIView {
    let plusButton = UIButton(type: .system)
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayout()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupLayout()
    }
    
    private func setupLayout() {
        // Style the view
        backgroundColor = .systemGray6
        layer.cornerRadius = 8
        clipsToBounds = true
        
        // Configure plus button
        plusButton.setTitle("+", for: .normal)
        plusButton.titleLabel?.font = .systemFont(ofSize: 24)
        plusButton.addTarget(self, action: #selector(plusTapped), for: .touchUpInside)
        
        // Add button constraints
        addSubview(plusButton)
        plusButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            plusButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            plusButton.centerYAnchor.constraint(equalTo: centerYAnchor),
            plusButton.widthAnchor.constraint(equalToConstant: 40),
            plusButton.heightAnchor.constraint(equalToConstant: 40),
            
            // Set fixed height for the item view (remove if you want auto-sizing)
            self.heightAnchor.constraint(equalToConstant: 60)
        ])
    }
    
    // Use a closure to pass tap events to the parent controller
    var onPlusTap: (() -> Void)?
    
    @objc private func plusTapped() {
        onPlusTap?()
    }
}

3. Implement Dynamic View Addition & Layout Updates

In your view controller, initialize the scrollview, contentView, and your initial secondView. Then add logic to insert new views whenever the plus button is tapped.

Example Swift code:

class ViewController: UIViewController {
    let scrollView = UIScrollView()
    let contentView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupScrollView()
        addInitialItemView()
    }
    
    private func setupScrollView() {
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.backgroundColor = .white
        
        scrollView.addSubview(contentView)
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            // ScrollView constraints
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
            scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            
            // ContentView constraints
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])
    }
    
    private func addInitialItemView() {
        let initialView = AddableItemView()
        initialView.onPlusTap = { [weak self] in
            self?.addNewItem(after: initialView)
        }
        contentView.addSubview(initialView)
        initialView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            initialView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
            initialView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            initialView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            initialView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16)
        ])
    }
    
    private func addNewItem(after previousView: AddableItemView) {
        let newView = AddableItemView()
        newView.onPlusTap = { [weak self] in
            self?.addNewItem(after: newView)
        }
        
        contentView.addSubview(newView)
        newView.translatesAutoresizingMaskIntoConstraints = false
        
        // Remove the old bottom constraint linking previous view to contentView
        NSLayoutConstraint.deactivate(contentView.constraints.filter {
            $0.firstItem === previousView && $0.secondItem === contentView && $0.firstAttribute == .bottom
        })
        
        // Add constraints for the new view
        NSLayoutConstraint.activate([
            newView.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: 16),
            newView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            newView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            newView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16)
        ])
        
        // Trigger immediate layout update so the scrollview adjusts right away
        contentView.layoutIfNeeded()
    }
}

Key Notes

  • We let Auto Layout handle the scrollview's content size: by updating the contentView's bottom constraint to point to the newest view, the contentView's height grows automatically, and the scrollview follows suit.
  • Using closures to pass tap events means every new view can trigger the addition of another view, creating the infinite chain you want.
  • If your views need to auto-size (e.g., for dynamic text), just remove the fixed height constraint from AddableItemView and let its internal content constraints define its height— the rest of the logic stays the same.

内容的提问来源于stack exchange,提问作者Aysel Abdullayeva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:51:33