UIScrollView内容插图,由子树之外的视图的高度定义

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

给出视图层次结构:

- View `container`
    - UIScrollView `scrollView`
        - UIView `content`
    - UIView `footer`

我希望UIScrollView.contentInset.bottom等于footer.bounds.height

问题:可以使用自动版面表达吗?

现在,我知道并且有一种非常明显的蛮力方法行得通:

  1. 观察boundsfooter属性的更改
  2. [scrollView.contentInset.bottom = -footer.bounds.height一旦footer的父母完成了layoutSubviews()

或者,我可以在content.bottomscrollView.bottom之间设置一个约束(据我所知,它表示底部内容插入为明确的大小内容),并且每次更改其常数footer范围改变。

但是关键是,所有这些方法都非常虚假,对于它们产生的可怕代码,确实让我感到不舒服,所以我想知道:

可以使用自动版面表达吗?

我尝试执行以下操作:

content.bottomAnchor.constraint(equalTo: footer.topAnchor)

[希望content.bottomAnchor被视为滚动视图内容的底部插图,但不是-自动布局实际上将其视为将内容的底部限制为页脚的顶部。

swift uiscrollview autolayout
1个回答
1
投票
确定-一种方法...

从iOS 11开始(我假设您不需要早于此定位),可以将UIScrollView的子视图限制为滚动视图的Frame Layout Guide。这样可以轻松地将非滚动UI元素添加到滚动视图层次结构中。

基于此层次结构:

- view - scrollView - contentView - element1 - element2 - element3 - UILayoutGuide - footerView

我们要做的是:

    将所有“可滚动”元素添加到contentView中
  • plus将一个UILayoutGuide添加到contentView中,它将作为或“底部”可滚动元素]
  • 将footerView添加到scrollView
  • last中,使其位于z顺序的顶部
  • 将footerView约束到scrollView的Frame Layout Guide,使其保持原状
  • 约束我们的UILayoutGuide的heightAnchor等于footerView的heightAnchor
  • 因为UILayoutGuide是非渲染视图,它将不可见,但是会在最后一个

    viewable元素的底部到contentView的底部之间创建空间-它将自动更改当/ footerView更改高度时的高度。

    这是一个完整的示例-scrollView / contentView / 3 imageViews /布局指南/半透明的footerView:

    class ExampleViewController: UIViewController { let scrollView: UIScrollView = { let v = UIScrollView() v.backgroundColor = .lightGray return v }() let contentView: UIView = { let v = UIView() v.backgroundColor = .cyan return v }() let footerView: UILabel = { let v = UILabel() v.textAlignment = .center v.textColor = .white v.font = UIFont.systemFont(ofSize: 24.0, weight: .bold) v.text = "Footer View" v.backgroundColor = UIColor.black.withAlphaComponent(0.65) return v }() var imgView1: UIImageView = { let v = UIImageView() v.backgroundColor = .red v.image = UIImage(systemName: "1.circle") v.tintColor = .white return v }() var imgView2: UIImageView = { let v = UIImageView() v.backgroundColor = .green v.image = UIImage(systemName: "2.circle") v.tintColor = .white return v }() var imgView3: UIImageView = { let v = UIImageView() v.backgroundColor = .blue v.image = UIImage(systemName: "3.circle") v.tintColor = .white return v }() override func viewDidLoad() { super.viewDidLoad() // add 3 image views as the content we want to see contentView.addSubview(imgView1) contentView.addSubview(imgView2) contentView.addSubview(imgView3) // add contentView to srollView scrollView.addSubview(contentView) // add footer view to scrollView last so it's at the top of the z-order scrollView.addSubview(footerView) view.addSubview(scrollView) [scrollView, contentView, footerView, imgView1, imgView2, imgView3].forEach { $0.translatesAutoresizingMaskIntoConstraints = false } // "spacer" for bottom of scroll content // we'll constrain it to the height of the footer view let spacerGuide = UILayoutGuide() contentView.addLayoutGuide(spacerGuide) let g = view.safeAreaLayoutGuide let svCLG = scrollView.contentLayoutGuide let scFLG = scrollView.frameLayoutGuide NSLayoutConstraint.activate([ // constrain scrollView view 40-pts on all 4 sides to view (safe-area) scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0), scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0), scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0), scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0), // contentView view 0-pts top / leading / trailing / bottom to scrollView contentLayoutGuide contentView.topAnchor.constraint(equalTo: svCLG.topAnchor, constant: 0.0), contentView.leadingAnchor.constraint(equalTo: svCLG.leadingAnchor, constant: 0.0), contentView.trailingAnchor.constraint(equalTo: svCLG.trailingAnchor, constant: 0.0), contentView.bottomAnchor.constraint(equalTo: svCLG.bottomAnchor, constant: 0.0), // contentView width == scrollView frameLayoutGuide width contentView.widthAnchor.constraint(equalTo: scFLG.widthAnchor, constant: 0.0), // imgView1 to top of contentView imgView1.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0.0), // imgView1 width / height imgView1.widthAnchor.constraint(equalToConstant: 240.0), imgView1.heightAnchor.constraint(equalToConstant: 240.0), // imgView1 centerX to contentView centerX imgView1.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), // imgView2 top to bottom of imgView1 + 20-pt spacing imgView2.topAnchor.constraint(equalTo: imgView1.bottomAnchor, constant: 20.0), // imgView2 width / height imgView2.widthAnchor.constraint(equalToConstant: 200.0), imgView2.heightAnchor.constraint(equalToConstant: 280.0), // imgView2 centerX to contentView centerX imgView2.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), // imgView3 top to bottom of imgView2 + 20-pt spacing imgView3.topAnchor.constraint(equalTo: imgView2.bottomAnchor, constant: 20.0), // imgView3 width / height imgView3.widthAnchor.constraint(equalToConstant: 280.0), imgView3.heightAnchor.constraint(equalToConstant: 320.0), // imgView3 centerX to contentView centerX imgView3.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), // spacerGuide top to bottom of actual content // spacerGuide top to imgView3 bottom spacerGuide.topAnchor.constraint(equalTo: imgView3.bottomAnchor, constant: 0.0), // spacerGuide to leading / trailing / bottom of contentView spacerGuide.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0.0), spacerGuide.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0.0), spacerGuide.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0.0), // footerView to leading / trailing / bottom of scrollView frameLayoutGuide // (constrained to frameLayoutGuide so it won't scroll) footerView.leadingAnchor.constraint(equalTo: scFLG.leadingAnchor, constant: 0.0), footerView.trailingAnchor.constraint(equalTo: scFLG.trailingAnchor, constant: 0.0), footerView.bottomAnchor.constraint(equalTo: scFLG.bottomAnchor, constant: 0.0), // footerView height == scrollView height with 0.25 multiplier // (so it will change height when scrollView changes height, such as device rotation) footerView.heightAnchor.constraint(equalTo: scFLG.heightAnchor, multiplier: 0.25), // finally, spacerGuide height equal to footerView height spacerGuide.heightAnchor.constraint(equalTo: footerView.heightAnchor), ]) } }

    结果:

    enter image description here

    滚动到底部:

    enter image description here

    并旋转(因此我们看到footerView的高度发生了变化)一直滚动到底部:

    enter image description here


    编辑

    对[[特定
    问题的答案是:不能。

    滚动视图的contentInset不是可以向其添加约束的对象……它是滚动视图的属性。就像您无法将滚动视图的.backgroundColor约束为自动布局约束一样。

  • © www.soinside.com 2019 - 2024. All rights reserved.