如何使用 autoLayout 创建具有水平和垂直分页的强大 ScrollView?

Posted

技术标签:

【中文标题】如何使用 autoLayout 创建具有水平和垂直分页的强大 ScrollView?【英文标题】:How do I create a robust ScrollView with both horizontal and vertical paging using autoLayout? 【发布时间】:2017-08-08 01:53:44 【问题描述】:

情况

所以我正在尝试制作一个UIScrollView,它基本上是一个菜单导航栏,用户可以通过在菜单项之间滑动来导航,其中有垂直布局的页面和水平布局的子页面。样机:

我这样做的方法是创建一个UIScrollView,其框架的大小为UILabel,并且我将isPagingEnabled 设置为true。然后我尝试添加一个UIStackView,每一行表示一个页面,每一行的内容是一个子页面。我将scrollView.contentSize 设置为UIStackView 的大小。问题是我所有标签的帧都是零,UIScrollView 不起作用。

:/

真的想避免寻求帮助,因为我觉得自己可以做到这一点,但我花了两天时间在这上面,我已经失去了所有希望。

代码

这是我添加标签的代码。它在滚动视图的超级视图init 上调用(因为UIScrollView 在自定义UIView 中,我称之为crossNavigation 视图)。

private func addScrollViewLabels() 
    
    //Get Max Number of Items in a Single Row
    var maxRowCount = -1
    for item in items 
        if (item.contents.count > maxRowCount) maxRowCount = item.contents.count
    
    
    self.rowsStackView.axis = .vertical
    self.rowsStackView.distribution = .fillEqually
    self.rowsStackView.alignment = .fill
    self.rowsStackView.translatesAutoresizingMaskIntoConstraints = false
    self.scrollView.addSubview(rowsStackView)
    
    for i in 0 ..< items.count 
        let row = items[i].contents
        let rowView = UIView()
        self.rowsStackView.addArrangedSubview(rowView)
        var rowLabels : [UILabel] = []
        //First Label
        rowLabels.append(UILabel())
        rowView.addSubview(rowLabels[0])
        
        NSLayoutConstraint(item: rowLabels[0], attribute: .leading, relatedBy: .equal, toItem: rowView, attribute: .leading, multiplier: 1.0, constant: 0.0).isActive = true
        NSLayoutConstraint(item: rowLabels[0], attribute: .top, relatedBy: .equal, toItem: rowView, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
        NSLayoutConstraint(item: rowLabels[0], attribute: .bottom, relatedBy: .equal, toItem: rowView, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
        NSLayoutConstraint(item: rowLabels[0], attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 0.55, constant: 0.0).isActive = true
        //Middle Labels
        for j in 1 ..< row.count 
            rowLabels.append(UILabel())
            rowView.addSubview(rowLabels[j])
            
            //Stick it to it's left
            NSLayoutConstraint(item: rowLabels[j], attribute: .leading, relatedBy: .equal, toItem: rowLabels[j-1], attribute: .trailing, multiplier: 1.0, constant: 0.0).isActive = true
            //Stick top to rowView
            NSLayoutConstraint(item: rowLabels[j], attribute: .top, relatedBy: .equal, toItem: rowView, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
            //Row Height is equal to rowView's Height
            NSLayoutConstraint(item: rowLabels[j], attribute: .height, relatedBy: .equal, toItem: rowView, attribute: .height, multiplier: 1.0, constant: 0.0).isActive = true
            //rowLabels[j].width = containerView.width * 0.55 (so other labels can peek around it)
            NSLayoutConstraint(item: rowLabels[j], attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 0.55, constant: 0.0).isActive = true
        
        
        //lastLabel.trailing = rowView.trailing
        NSLayoutConstraint(item: rowLabels[row.count-1], attribute: .trailing, relatedBy: .equal, toItem: rowView, attribute: .trailing, multiplier: 1.0, constant: 0.0).isActive = true
    

    //Constraints for stack view:
    //rowsStackView.height = scrollView.height * items.count
    NSLayoutConstraint(item: rowsStackView, attribute: .height, relatedBy: .equal, toItem: scrollView, attribute: .height, multiplier: CGFloat(self.items.count), constant: 0.0).isActive = true
    //rowsStackView.height = scrollView.height * items.count
    NSLayoutConstraint(item: rowsStackView, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .leading, multiplier: 1.0, constant: 0.0).isActive = true
    NSLayoutConstraint(item: rowsStackView, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
    NSLayoutConstraint(item: rowsStackView, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 1.0, constant: 0.0).isActive = true
    
    self.scrollView.contentSize = rowsStackView.frame.size

【问题讨论】:

你看过 UICollectionViewController 类了吗? 是的,可惜没有分页功能 UICollectionView 继承自 UIScrollView 分页有没有 【参考方案1】:

看看我根据你的模型制作的demo project。 它为您想做的所有事情建立了框架。

检查视图层次结构,您将了解在何处配置布局以使其完全符合您的要求。

最后一步是调整分页,以便获得所需的快照行为。有很多方法可以做到这一点,但它有点复杂。

祝你好运!

【讨论】:

哇,这比我拥有的好多了!它具有独立的行滚动!真棒!谢谢斯特凡! 滚动不是问题,因为有多个资源(包括 SO)有关于如何做到这一点的指南。对于未来迷路的旅行者,请将此作为开始:***.com/questions/1220354/…

以上是关于如何使用 autoLayout 创建具有水平和垂直分页的强大 ScrollView?的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin iOS Autolayout:自动调整各种设备的宽度和垂直滚动,同时保持水平滚动禁用演示链接

AutoLayout:垂直到水平,没有明确的尺寸?

Autolayout-处理七个按钮的圆形,不影响前导、尾随、垂直和水平间距

创建具有水平和垂直滚动的 RecyclerView

如何以编程方式设置 UIScrollView 以使用 autoLayout 水平滚动

Autolayout - 当一个视图具有动态高度时,在 UITableViewCell 中垂直居中两个视图