提问人:Shawn Frank 提问时间:9/11/2023 最后编辑:Shawn Frank 更新时间:9/17/2023 访问量:83
UIStackView 或 UITableView 内的 UIPageViewController [已关闭]
UIPageViewController inside a UIStackView or UITableView [closed]
问:
我正在尝试使用 UIKit 和 Storyboard 创建入职 UI。我有一个要求,即某些部分需要静态/粘性,而某些部分必须在用户在入职时水平滚动。
这应该给你一个更好的主意:
为了实现上述目的,我使用了在情节提要中设置的以下视图层次结构
UIView控制器
- 跳过按钮
- 包含用于水平滑动的 UIPageViewController 的容器视图
- UIView控制器
- UIScrollView(这是另一个滚动视图,而不是页面视图控制器的)
- UIStack视图
- UIImage(大红十字)
- UILabel的
- UILabel的
- UIStack视图
- UIScrollView(这是另一个滚动视图,而不是页面视图控制器的)
- UIView控制器
- UIStack视图
- 页面指示器
- “订阅”按钮
- 登录按钮
虽然这在正常设置中工作正常,但我需要支持用户可以从设置/控制面板更新的动态/大文本。
我现在遇到的问题是内容在UIPageViewController内的滚动视图中增长,如图所示
上面的白框不是 UI 的一部分,而只是我试图表明垂直滚动仅限于该区域。
我明白,由于我的设置,这是正确的行为。
我的问题/目标: 什么是更好的布局结构,以便在激活较大的文本字体时整个视图增长,从而使视图作为一个整体滚动,而不是滚动被限制在屏幕的一小部分,这意味着页面指示器和底部按钮位于屏幕下方以获得更大的文本大小。
请记住,只有图片、标题和描述是可水平滚动的。
我试图将 scrollview 和 stackview 从 PageViewController 中取出,以设置如下内容:
UIView控制器
- UIScroll视图
- UIStack视图
- 跳过按钮
- 包含用于水平滑动的 UIPageViewController 的容器视图
- UIView控制器
- UIStack视图
- UIImage(大红十字)
- UILabel的
- UILabel的
- UIStack视图
- UIView控制器
- UIStack视图
- 页面指示器
- “订阅”按钮
- 登录按钮
- UIStack视图
但是,由于我使用的是自动布局,因此我在情节提要中收到一个错误,指出无法确定页面视图控制器的高度。
如果无法通过故事板或程序化设置,那么在故事板中或以编程方式设置它的正确方法是什么?
如果这样可以让生活更轻松,我愿意切换到 tableview 或 collectionview。
答:
经过评论讨论,我想我理解了你的最终目标。
您正在创建一个“入职”UI,因此我将做出一些假设......
- 可能不是几十个“页面”
- 可能是相当“静态”的页面(每个“页面”上没有或很少用户交互)
因此,如果“页面”不是动态分配的,我们不必担心内存问题。
A 是一个不错的组件 -- 但是,它在“幕后”做了很多工作。特别是,它设置“页面框架”以匹配其框架。在您的例子中,您希望其框架的高度增加以匹配页面高度。UIPageViewController
这可能是可以做到的,但不使用页面视图控制器会容易得多。
相反,让我们使用标准的 .UIScrollView
首先,如果我们在 中嵌入 ,并将视图的 Height 约束等于标签的 Height(假设我们从表到视图有 8 点约束):UILabel
UIView
someView.heightAnchor.constraint(equalTo: someLabel.heightAnchor, constant: 16.0).isActive = true
黄色视图的高度将随着标签的增长而增加:
我们可以用一个(红色虚线的轮廓是滚动视图框架)做同样的事情:UIScrollView
滚动视图框架 Height 随着标签高度的增加而增长,我们仍然可以水平滚动,但不能垂直滚动。
因此,让我们首先将“页面”放入水平堆栈视图中:
,然后将该堆栈视图嵌入到滚动视图中。
如果我们将每个页面的 Width 限制为滚动视图的 Width,并在滚动视图上设置一个 Height 约束,我们将得到这个(粗黑色虚线轮廓是滚动视图框架):
到目前为止,没什么特别的。
但是,如果页面高度发生变化 - 例如在标签上使用 - 而不做任何其他操作 - 我们会得到这个:.adjustsFontForContentSizeCategory - true
这会导致垂直和水平滚动 - 正如预期的那样。
但是,对于目标布局,我们希望滚动视图的 Height 增长以匹配新的页面 Height。
让我们将滚动视图的 Height 限制为堆栈视图的 Height:
NSLayoutConstraint.activate([
// "normal" Top/Leading/Trailing constraints
pageScrollView.topAnchor.constraint(equalTo: view.topAnchor),
pageScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
pageScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// instead of setting a Height related to the view, or a constant value
// we'll constrain the Height to the *stack view's* height
pageScrollView.heightAnchor.constraint(equalTo: pageStackView.heightAnchor),
])
现在,随着“页面”高度的增加,滚动视图框架也会增长:
一旦我们将我们的嵌入到“外部”滚动视图中,并相对于其他“外部”UI 元素对其进行约束,我们将到达目标布局。pageScrollView
纯黑色轮廓是“iPhone”框架;黄色长虚线轮廓是“外部”滚动视图框架;白色虚线轮廓是“页面”滚动视图框架:
随着“页面”高度的增长......
我们将能够垂直滚动整个“外部视图”......
并继续水平滚动“页面”。
最终结果 - (按比例缩小,以便在此处发布):
最后,一些示例代码。一切都是通过代码完成的,因此不需要复杂的故事板设计或/或连接:@IBOutlet
@IBAction
“页面”数据的简单结构:
struct PageData {
var title: String = ""
var desc: String = ""
// maybe different images on each page?
var imgName: String = ""
}
示例“页面”视图控制器 - 我们将将其添加为子项并获取视图。ImageView、Title 和 Description 标签:
class SamplePageVC: UIViewController {
let titleLabel: UILabel = UILabel()
let descLabel: UILabel = UILabel()
let imgView: UIImageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
guard let customFont1 = UIFont(name: "TimesNewRomanPSMT", size: 32.0),
let customFont2 = UIFont(name: "Verdana", size: 16.0)
else {
fatalError("Could not load font!")
}
titleLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont1)
descLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont2)
[titleLabel, descLabel].forEach { v in
v.adjustsFontForContentSizeCategory = true
v.textAlignment = .center
v.textColor = .white
v.numberOfLines = 0
v.setContentHuggingPriority(.required, for: .vertical)
v.setContentCompressionResistancePriority(.required, for: .vertical)
}
// might want to set a MAX Content Size Category?
//view.maximumContentSizeCategory = .accessibilityExtraLarge
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .center
stackView.spacing = 6
[imgView, titleLabel, descLabel].forEach { v in
stackView.addArrangedSubview(v)
}
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
let bc: NSLayoutConstraint = stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0)
bc.priority = .required - 1
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(lessThanOrEqualTo: g.bottomAnchor, constant: -8.0),
bc,
imgView.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -60.0),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0),
titleLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -20.0),
descLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -20.0),
])
}
}
示例“载入”视图控制器 - 使用上面讨论的所有内容:
class OnboardingVC: UIViewController, UIScrollViewDelegate {
let skipBtn: UIButton = UIButton()
let subscribeBtn: UIButton = UIButton()
let loginBtn: UIButton = UIButton()
let pgCtrl: UIPageControl = UIPageControl()
let outerScrollView: UIScrollView = UIScrollView()
let outerContentView: UIView = UIView()
let pageScrollView: UIScrollView = UIScrollView()
let pageStackView: UIStackView = UIStackView()
var pageData: [PageData] = [
PageData(title: "First Page", desc: "Some description about it.", imgName: "pgXC"),
PageData(title: "Short", desc: "This page has somewhat longer description text.", imgName: ""),
PageData(title: "A Longer Title", desc: "This page will have even more text in the description label. That will help demonstrate the height matching and resulting layout / scrolling changes.", imgName: ""),
PageData(title: "Final", desc: "This is the last page.", imgName: ""),
]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .init(red: 1.0, green: 0.25, blue: 0.2, alpha: 1.0)
guard let skipFont = UIFont(name: "Verdana", size: 17.0),
let btnFont = UIFont(name: "Verdana-Bold", size: 16.0)
else {
fatalError("Could not load font!")
}
skipBtn.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: skipFont)
subscribeBtn.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: btnFont)
loginBtn.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: btnFont)
for (btn, str) in zip([skipBtn, subscribeBtn, loginBtn], ["Skip", "Subscribe", "Login"]) {
btn.titleLabel?.adjustsFontForContentSizeCategory = true
btn.setTitle(str, for: [])
btn.layer.cornerRadius = 6
}
skipBtn.setTitleColor(.white, for: .normal)
skipBtn.setTitleColor(.lightGray, for: .highlighted)
skipBtn.setContentCompressionResistancePriority(.required, for: .vertical)
subscribeBtn.setTitleColor(.blue, for: .normal)
subscribeBtn.setTitleColor(.systemBlue, for: .highlighted)
subscribeBtn.backgroundColor = .white
loginBtn.setTitleColor(.white, for: .normal)
loginBtn.setTitleColor(.lightGray, for: .highlighted)
loginBtn.layer.borderColor = UIColor.white.cgColor
loginBtn.layer.borderWidth = 1
let btnStack: UIStackView = UIStackView()
btnStack.axis = .vertical
btnStack.spacing = 12
// let's add top and bottom padding for the buttons
// we're not using UIButtonConfiguration so we ignore the deprecation warnings
[subscribeBtn, loginBtn].forEach { v in
var edges = v.contentEdgeInsets
edges.top = 12.0
edges.bottom = 12.0
v.contentEdgeInsets = edges
}
// add Page Control and bottom buttons to vertical stack view
// set Hugging and Compression priorities to .required so they
// won't stretch or collapse vertically
[pgCtrl, subscribeBtn, loginBtn].forEach { v in
btnStack.addArrangedSubview(v)
v.setContentHuggingPriority(.required, for: .vertical)
v.setContentCompressionResistancePriority(.required, for: .vertical)
}
// add "pages" stack view to "page" scroll view
pageStackView.translatesAutoresizingMaskIntoConstraints = false
pageScrollView.addSubview(pageStackView)
// add elements to outerContentView
[skipBtn, pageScrollView, btnStack].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
outerContentView.addSubview(v)
}
// add outerContentView to outerScrollView
outerContentView.translatesAutoresizingMaskIntoConstraints = false
outerScrollView.addSubview(outerContentView)
// add outerScrollView to (self) view
outerScrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(outerScrollView)
let g = view.safeAreaLayoutGuide
var cg = pageScrollView.contentLayoutGuide
var fg = pageScrollView.frameLayoutGuide
NSLayoutConstraint.activate([
// constrain all 4 sides of pageStackView to pageScrollView.contentLayoutGuide
pageStackView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 0.0),
pageStackView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 0.0),
pageStackView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: 0.0),
pageStackView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: 0.0),
// constrain skip button Top/Trailing
skipBtn.topAnchor.constraint(equalTo: outerContentView.topAnchor, constant: 12.0),
skipBtn.trailingAnchor.constraint(equalTo: outerContentView.trailingAnchor, constant: -40.0),
// constrain pageScrollView
// Top 8-points below skip button Bottom
// Leading/Trailing to outerContentView (so, full width)
pageScrollView.topAnchor.constraint(equalTo: skipBtn.bottomAnchor, constant: 8.0),
pageScrollView.leadingAnchor.constraint(equalTo: outerContentView.leadingAnchor, constant: 0.0),
pageScrollView.trailingAnchor.constraint(equalTo: outerContentView.trailingAnchor, constant: 0.0),
// constrain pageScrollView HEIGHT to pageStackView HEIGHT
// now, the scroll view Height will match the "pages" height
pageScrollView.heightAnchor.constraint(equalTo: pageStackView.heightAnchor, constant: 0.0),
// constrain btnStack
// Top >= pageScrollView Bottom plus a little "padding space"
// Leading/Trailing to outerContentView plus a little "padding space" on the sides
// Bottom to outerContentView plus a little "padding space"
btnStack.topAnchor.constraint(greaterThanOrEqualTo: pageScrollView.bottomAnchor, constant: 12.0),
btnStack.leadingAnchor.constraint(equalTo: outerContentView.leadingAnchor, constant: 20.0),
btnStack.trailingAnchor.constraint(equalTo: outerContentView.trailingAnchor, constant: -20.0),
btnStack.bottomAnchor.constraint(equalTo: outerContentView.bottomAnchor, constant: -12.0),
])
cg = outerScrollView.contentLayoutGuide
fg = outerScrollView.frameLayoutGuide
// we want outerContentView to be the same Height as outerScrollView
// but less-than-required Priority so it can grow based on its subviews
let hc: NSLayoutConstraint = outerContentView.heightAnchor.constraint(equalTo: fg.heightAnchor, constant: 0.0)
hc.priority = .required - 1
NSLayoutConstraint.activate([
// constrain all 4 sides of outerContentView to outerScrollView.contentLayoutGuide
outerContentView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 0.0),
outerContentView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 0.0),
outerContentView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: 0.0),
outerContentView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: 0.0),
// outerContentView Width to outerScrollView.frameLayoutGuide Width
// so we will never get horizontal scrolling
outerContentView.widthAnchor.constraint(equalTo: fg.widthAnchor, constant: 0.0),
hc,
// constrain all 4 sides of outerScrollView to (self) view
outerScrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
outerScrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
outerScrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
outerScrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
// add page view VCs for each data item
// we could do this with a "SamplePageView" UIView subclass
// but this shows how to use UIViewController if that's how the "pages" are setup
for (idx, d) in pageData.enumerated() {
let vc = SamplePageVC()
addChild(vc)
// add its view to pageStackView
pageStackView.addArrangedSubview(vc.view)
vc.didMove(toParent: self)
vc.titleLabel.text = d.title
vc.descLabel.text = d.desc
if !d.imgName.isEmpty, let img = UIImage(named: d.imgName) {
vc.imgView.image = img
} else if let img = UIImage(systemName: "\(idx).circle.fill") {
vc.imgView.image = img
vc.imgView.tintColor = .orange
}
// each page view Width is equal to pageScrollView.frameLayoutGuide width
vc.view.widthAnchor.constraint(equalTo: pageScrollView.frameLayoutGuide.widthAnchor, constant: 0.0).isActive = true
}
// we *probably* do not want to see scroll indicators
outerScrollView.showsHorizontalScrollIndicator = false
outerScrollView.showsVerticalScrollIndicator = false
pageScrollView.showsHorizontalScrollIndicator = false
pageScrollView.showsVerticalScrollIndicator = false
// enable paging for the "pages"
pageScrollView.isPagingEnabled = true
// we will implement scrollViewDidScroll() so we can update the page control
// when the user drags the pages left/right
pageScrollView.delegate = self
pgCtrl.numberOfPages = pageData.count
pgCtrl.addTarget(self, action: #selector(changePage(_:)), for: .valueChanged)
}
@objc func changePage(_ sender: UIPageControl) {
let x: CGFloat = pageScrollView.frame.width * CGFloat(sender.currentPage)
UIView.animate(withDuration: 0.3, animations: {
self.pageScrollView.contentOffset.x = x
})
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == pageScrollView {
let pg: Int = Int(floor((scrollView.contentOffset.x + scrollView.frame.width * 0.5) / scrollView.frame.width))
pgCtrl.currentPage = pg
}
}
}
示例开发模式“载入”视图控制器 - 子类的子类着色并勾勒出 UI 元素的轮廓。它将生成上面所示的屏幕帽(在 iPad 上运行时),以便更轻松地查看正在发生的事情:OnboardingVC
// MARK: subclass of OnboardingVC
// for use during development
// colorizes and outlines view elements
// centers a "simulated" device frame so we can see "outside the frame"
class DevOnboardingVC: OnboardingVC {
override func viewDidLoad() {
super.viewDidLoad()
let g = view.safeAreaLayoutGuide
// we need to re-build the outerScrollView constraints
outerScrollView.removeFromSuperview()
view.addSubview(outerScrollView)
// we'll make the "device frame"
// 90% of the view height, or
// 640-points, whichever is smaller
let targetHeightC: NSLayoutConstraint = outerScrollView.heightAnchor.constraint(equalTo: g.heightAnchor, multiplier: 0.9)
targetHeightC.priority = .required - 1
let maxHeightC: NSLayoutConstraint = outerScrollView.heightAnchor.constraint(lessThanOrEqualToConstant: 640.0)
NSLayoutConstraint.activate([
outerScrollView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
outerScrollView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
outerScrollView.widthAnchor.constraint(equalToConstant: 300.0),
maxHeightC, targetHeightC,
])
view.backgroundColor = UIColor(white: 0.90, alpha: 1.0)
outerContentView.backgroundColor = .init(red: 1.0, green: 0.25, blue: 0.2, alpha: 1.0)
outerScrollView.clipsToBounds = false
pageScrollView.clipsToBounds = false
pageStackView.arrangedSubviews.forEach { v in
v.backgroundColor = .white.withAlphaComponent(0.5)
v.backgroundColor = .init(red: 1.0, green: 0.25, blue: 0.2, alpha: 1.0).withAlphaComponent(0.5)
v.layer.borderColor = UIColor.systemBlue.cgColor
v.layer.borderWidth = 2
}
self.children.forEach { vc in
if let vc = vc as? SamplePageVC {
vc.titleLabel.backgroundColor = .systemGreen
vc.descLabel.backgroundColor = .systemGreen
}
}
let dashV1 = DashedBorderView()
dashV1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dashV1)
let dashV2 = DashedBorderView()
dashV2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dashV2)
let dashV3 = DashedBorderView()
dashV3.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dashV3)
NSLayoutConstraint.activate([
dashV1.topAnchor.constraint(equalTo: outerScrollView.topAnchor, constant: 0.0),
dashV1.leadingAnchor.constraint(equalTo: outerScrollView.leadingAnchor, constant: 0.0),
dashV1.trailingAnchor.constraint(equalTo: outerScrollView.trailingAnchor, constant: 0.0),
dashV1.bottomAnchor.constraint(equalTo: outerScrollView.bottomAnchor, constant: 0.0),
dashV2.topAnchor.constraint(equalTo: outerScrollView.topAnchor, constant: 0.0),
dashV2.leadingAnchor.constraint(equalTo: outerScrollView.leadingAnchor, constant: 0.0),
dashV2.trailingAnchor.constraint(equalTo: outerScrollView.trailingAnchor, constant: 0.0),
dashV2.bottomAnchor.constraint(equalTo: outerScrollView.bottomAnchor, constant: 0.0),
dashV3.topAnchor.constraint(equalTo: pageScrollView.topAnchor, constant: 0.0),
dashV3.leadingAnchor.constraint(equalTo: pageScrollView.leadingAnchor, constant: 0.0),
dashV3.trailingAnchor.constraint(equalTo: pageScrollView.trailingAnchor, constant: 0.0),
dashV3.bottomAnchor.constraint(equalTo: pageScrollView.bottomAnchor, constant: 0.0),
])
dashV1.color = .black
dashV1.lineWidth = 16
dashV1.position = .outside
dashV1.dashPattern = []
dashV2.color = .systemYellow
dashV2.lineWidth = 3
dashV2.dashPattern = [60, 8]
dashV3.color = .white
dashV3.lineWidth = 2
}
}
虚线边框视图 - 由“Dev Mode”类使用:
class DashedBorderView: UIImageView {
enum BorderPosition {
case inside, middle, outside
}
public var position: BorderPosition = .middle { didSet { setNeedsLayout() } }
public var dashPattern: [NSNumber] = [16, 16] { didSet { dashedLineLayer.lineDashPattern = dashPattern } }
public var lineWidth: CGFloat = 1.0 { didSet { dashedLineLayer.lineWidth = lineWidth } }
public var color: UIColor = .red { didSet { dashedLineLayer.strokeColor = color.cgColor } }
override class var layerClass: AnyClass { CAShapeLayer.self }
var dashedLineLayer: CAShapeLayer { layer as! CAShapeLayer }
init() {
super.init(frame: .zero)
commonInit()
}
override init(image: UIImage?) {
super.init(image: image)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// this view will usually be overlaid on top of an interactive view
// so disable by default
isUserInteractionEnabled = false
dashedLineLayer.fillColor = UIColor.clear.cgColor
dashedLineLayer.strokeColor = color.cgColor
dashedLineLayer.lineWidth = lineWidth
dashedLineLayer.lineDashPattern = dashPattern
}
override func layoutSubviews() {
super.layoutSubviews()
switch position {
case .inside:
dashedLineLayer.path = CGPath(rect: bounds.insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5), transform: nil)
case .outside:
dashedLineLayer.path = CGPath(rect: bounds.insetBy(dx: -lineWidth * 0.5, dy: -lineWidth * 0.5), transform: nil)
case .middle:
dashedLineLayer.path = CGPath(rect: bounds, transform: nil)
}
}
}
重要提示
- 这只是示例代码!!它不打算,也不应被视为“生产就绪”
- 设计是针对 iPhone 纵向的......它仍然可以在 iPad 或旋转为横向的手机上工作,但我保证它看起来不会很好:)
我还在这里发布了一个包含上述所有代码的项目:https://github.com/DonMag/PageViewApproach
编辑 - 更多解释...
使用 or(无论是使用流程、自定义还是组合布局)的最大好处是内存管理......您可以拥有 100 个“页面”,而不必担心。UIPageViewController
UICollectionView
但是,对于这个布局目标,动态高度始终是问题所在......因为这两个类也被设计为根据我们为 PageViewController 或 CollectionView 设置的框架来“布局它们的页面/单元格”。
考虑 5 个“页面”,如下所示:
在页面视图控制器或集合视图中,我们无法设置帧高度(黄色矩形),因为在实例化第 4 页之前,我们不知道“所需的最大高度”。
我们可以计算负载的最大高度,例如:
func getMaxHeight() -> CGFloat {
var maxHeight: CGFloat = 0.0
for i in 0..<numPages {
let v = MyPageView()
v.fillLabels(forPage: i)
let sz = v.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
maxHeight = max(maxHeight, sz.height)
}
return maxHeight
}
或者,如果我们在内存中“缓存”了页面视图:
func getMaxHeight() -> CGFloat {
var maxHeight: CGFloat = 0.0
pageViews.forEach { v in
let sz = v.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
maxHeight = max(maxHeight, sz.height)
}
return maxHeight
}
由于您使用的是动态类型,因此我们还需要在更改时调用它并更新帧高度。UITraitCollection.preferredContentSizeCategory
就我个人而言,我会采用滚动视图方法,并简单地让自动布局为我处理它。
评论