使 UIView 垂直拉伸到一定边距

Make UIView stretch vertically until a certain margin

提问人:Avi Sasson 提问时间:8/15/2023 更新时间:11/13/2023 访问量:45

问:

我有一个内容可能垂直溢出的地方,我设置了一个垂直约束(高度约束,但顶部/底部约束具有相同的效果)以防止整个视图(弹出窗口)溢出。UIView

我的目标是让视图占据其内容所需的高度,直到它从屏幕边缘(安全区域)进入预先配置的顶部和底部边距。

我的问题是,一旦我将设备旋转到横向并旋转回纵向,弹出窗口就不会像垂直那样拉伸。

我已经尝试过的事情:

  1. 为高度约束设置高优先级。
  2. 在 上设置高抗压性。itemView
  3. 更新约束后调用。layoutIfNeeded
  4. 创建另一个优先级较低的高度约束,告诉视图完全拉伸。

显然,这些都没有奏效。

初始状态:

enter image description here

旋转后:

enter image description here

旋转回纵向:

enter image description here

代码如下所示:

class PopupView: UIView, SpinnerProvider {
    lazy var spinner: SNSpinnerView = {
        let spinner = SNSpinnerView()
        spinner.translatesAutoresizingMaskIntoConstraints = false
        spinner.spinnerImageView.image = spinnerImageDefault
        spinner.isHidden = false
        return spinner
    }()
    
    var itemView = UIView() {
        didSet {
            spinner.isHidden = true
            UIView.animate(withDuration: Constants.animationDuration) {
                self.backgroundColor = .snDarkDimming
            }
            popupContainerView.addChild(itemView)
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    private lazy var popupContainerView: UIView = {
        let view = UIView(frame: .zero)
        view.layer.cornerRadius = Constants.popUpFrameCornerRadius
        view.layer.masksToBounds = true
        view.accessibilityIdentifier = "popup"
        return view
    }()
        
    private lazy var containerHeightConstraint: NSLayoutConstraint = {
        // Constant will change.
        popupContainerView.heightAnchor.constraint(lessThanOrEqualToConstant: 0)
    }()
    
    private lazy var containerLeadingConstraint: NSLayoutConstraint = {
        popupContainerView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor)
    }()
    
    private lazy var containerTrailingConstraint: NSLayoutConstraint = {
        popupContainerView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor)
    }()
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setup() {
        backgroundColor = .snLoadingViewBackground
        useAutoLayout()
        addSubview(popupContainerView)
        popupContainerView.useAutoLayout()
            .center(in: self)
        
        addSubview(spinner)
        spinner.useAutoLayout()
            .center(in: self)
        spinner.startRotationAnimation()
        
        NSLayoutConstraint.activate([
            containerHeightConstraint,
            containerLeadingConstraint,
            containerTrailingConstraint
        ])
        
        adjustConstraints()
    }
    
    func adjustConstraints() {
        if UIDevice.current.orientation.isPortrait {
            containerLeadingConstraint.constant = Constants.Portrait.leadingInset
            containerTrailingConstraint.constant = Constants.Portrait.trailingInset
            containerHeightConstraint.constant = UIScreen.main.bounds.height - safeAreaLayoutGuide.layoutFrame.height - (Constants.Portrait.horizontalInset * 2)
        } else {
            containerLeadingConstraint.constant = Constants.Landscape.leadingInset
            containerTrailingConstraint.constant = Constants.Landscape.trailingInset
            containerHeightConstraint.constant = UIScreen.main.bounds.height - (Constants.Landscape.horizontalInset * 2)
        }
    }
}

adjustConstraints从父视图调用:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        popupView.adjustConstraints()
    }
iOS Swift UIView UIKit 约束

评论

0赞 Larme 8/15/2023
当你调用它时,当它起作用时,当它不起作用时,值是什么?你应该检查参数吗?UIScreen.main.boundsadjustConstraints()sizeviewWillTransition()
0赞 DonMag 8/16/2023
不要使用...相反,请比较 SuperView 的大小。如果高度>宽度,则将其视为“纵向”方向。当用户使用多任务处理在 iPad 上运行您的应用程序时,这将有所帮助。你没有说你打算在iPad上全屏运行时做什么 - 我假设你想要一个“最大”的宽度/高度?顺便说一句,当我们不得不猜测这么多事情时,很难帮助弄清楚可能出了什么问题。查看最小可重现示例UIDevice.current.orientation.isPortrait
0赞 matt 8/16/2023
除了小于或等于的约束外,还需要将相等约束设置为最大高度,但优先级较低。这为视图提供了一些目标。
0赞 DonMag 8/18/2023
您还在寻求这方面的帮助吗?㞖。。。假设您的“项目视图”将有一个单行/固定高度的“标题标签”+ 3 个固定高度的按钮 + 一个可变高度的“正文”(例如多行标签)......如果太高怎么办?“正文”文本是否应该被截断,就像你的图像一样?或者,也许是可滚动的?

答:

0赞 deniz 11/13/2023 #1

以模态方式呈现的视图基本上受到约束。我在这里回答了类似的问题。由于布局引擎受到约束,因此在初始加载期间以一种方式求解布局,然后在返回纵向模式时以另一种方式求解布局。当视图受到约束或模棱两可时,就会发生这种情况。

如果你适当地约束你的观点,那么你就不会有这个问题。我复制了您的视图,其中包含一组正确的约束和 UIStackView 的战略性使用。请参阅以下结果:

Modal view with properly constrained views

完整的解决方案真的很长,所以我只会发布最相关的部分。

  1. 我首先创建了一个带有圆角和白色背景的 UIView。我称之为容器视图。我将此容器视图作为唯一的子视图添加到视图控制器的视图中。
  2. 然后,此容器视图还有一个子视图:UIStackView,周围有一些填充。
  3. 然后,我在这个 StackView 中嵌入了其他所有内容。在此示例中,我从上到下有 UILabel、UITexView、UIButton、UIButton、UIButton。
  4. 确保 StackView 中的标题标签 (RITM0010007) 具有固定高度。在我的示例中,我将此高度定义为 45 磅。
  5. 如果使用 UITextView 作为主要内容,请确保它不可滚动。可以使用 UITextView 或 UILabel 作为主要内容。不要对此定义任何约束。
  6. 确保堆栈视图中的按钮也定义了高度。我有 36 分。
  7. 确保 Stackview 的对齐方式为填充。
  8. Stackview 负责处理其余缺失的约束。

以下是在代码中创建容器视图的方式:

private func configureContainerView() {
    //containerView is declared at the view controller level
    containerView = UIView()
    containerView.backgroundColor = .systemBackground
    containerView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMaxXMinYCorner]
    containerView.layer.cornerRadius = 12
    containerView.layer.borderColor = UIColor.black.cgColor
    containerView.layer.borderWidth = 1
    containerView.clipsToBounds = true
    
    containerView.translatesAutoresizingMaskIntoConstraints = false
    self.view.addSubview(containerView)
    NSLayoutConstraint.activate([
        containerView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
        containerView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
        containerView.topAnchor.constraint(greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 20),
        containerView.bottomAnchor.constraint(lessThanOrEqualTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
        containerView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor)
    ])
}

这是在代码中创建堆栈视图的方式:

private func configureStackView() {
    //primaryStackView is declared at the view controller level
    primaryStackView = UIStackView()
    primaryStackView.axis = .vertical
    primaryStackView.spacing = 16
    primaryStackView.alignment = .fill
    primaryStackView.translatesAutoresizingMaskIntoConstraints = false
    self.containerView.addSubview(primaryStackView)
    
    NSLayoutConstraint.activate([
        primaryStackView.leadingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.leadingAnchor, constant: 20),
        primaryStackView.trailingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.trailingAnchor, constant: -20),
        primaryStackView.topAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.topAnchor, constant: 20),
        primaryStackView.bottomAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.bottomAnchor, constant: -20)
    ])
}

确保将其余内容添加到堆栈视图中,如下所示:

private func configureTitleView() {
    let titleLabel = UILabel()
    //configure title label
    primaryStackView.addArrangedSubview(titleLabel)
    NSLayoutConstraint.activate([
        titleLabel.heightAnchor.constraint(equalToConstant: 45)
    ])
}