【问题标题】:How to place N number of views Horizontally in scrollview using auto layout programmatically?如何以编程方式使用自动布局在滚动视图中水平放置 N 个视图?
【发布时间】:2017-05-19 13:25:00
【问题描述】:

我想在 scrollview 中水平创建 N 个视图。所以我创建了一个 Temporary View 并将 current view 分配给它。然后我根据它放置约束。只有一个视图显示其他视图未显示。 我想要两个视图之间的水平空间。 我正在使用 VFL 设置自动布局。 这是我尝试的代码。

func ect() {
        let scrollHeight = self.scrollObj.frame.size.height - 40
        let scrollHeightString = String(describing: scrollHeight)
        print(scrollHeightString)
        var firstTextView = UIView() // this is temporary view
        var columnIndex: CGFloat = 0
        for  _ in 0 ..< 5 {
            let textViewSample = UITextView()
            textViewSample.translatesAutoresizingMaskIntoConstraints = false
            //textViewSample.attributedText = self.htmlString.utf8Data?.attributedString
            textViewSample.isScrollEnabled = false
            textViewSample.tag = Int(columnIndex)
            textViewSample.backgroundColor = UIColor.green
            self.scrollObj.addSubview(textViewSample)

            if columnIndex == 0{
//here i am setting first view to left margin.
            self.scrollObj.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[textViewSample]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["textViewSample":textViewSample]))

                //height to textview
                self.scrollObj.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[textViewSample(200)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["textViewSample":textViewSample]))

                //width to textview
                self.scrollObj.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[textViewSample(200)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["textViewSample":textViewSample]))

            }else{

             //setting horizontal space to Temp view and current view   
            self.scrollObj.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[firstTextView]-(10)-[textViewSample]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["textViewSample":textViewSample,"firstTextView":firstTextView]))
                //setting equal width and equal height to second textview based on first
                self.scrollObj.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[firstTextView][textViewSample(==firstTextView)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["textViewSample":textViewSample,"firstTextView":firstTextView]))

            }

            //y position to textview
            self.scrollObj.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[textViewSample]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["textViewSample":textViewSample]))

            firstTextView = textViewSample//assigning current view to temp view.
            // Increase the current offset
            columnIndex = columnIndex + 1
            }
        self.scrollObj.contentSize = CGSize(width: columnIndex*200, height: columnIndex*200)
        self.scrollObj .setNeedsLayout()
        self.scrollObj.layoutIfNeeded()
    }

这就是我的设计:-

【问题讨论】:

  • 尽可能使用 UIStackView
  • 我需要放置一个动态视图,我没有堆栈视图的经验,这就是我想要的。

标签: ios ipad autolayout swift3


【解决方案1】:

如果你想使用约束,你可以这样做:

var previousAnchor = scrollView.leadingAnchor

for _ in 0 ..< 5 {
    let textView = UITextView()
    textView.translatesAutoresizingMaskIntoConstraints = false
    textView.backgroundColor = .green
    textView.isScrollEnabled = false
    scrollView.addSubview(textView)

    NSLayoutConstraint.activate([
        textView.leadingAnchor.constraint(equalTo: previousAnchor, constant: 10),
        textView.heightAnchor.constraint(equalToConstant: 200),
        textView.widthAnchor.constraint(equalToConstant: 200),
        textView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 10),
        textView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -10)
    ])

    previousAnchor = textView.trailingAnchor
}

previousAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true

从概念上讲,它与您所拥有的相似:因此,对于每个文本视图,将宽度设置为 200,高度设置为 200,将前一个视图的空间设置为 10。对于第一个视图,距离滚动的前缘为 10视图,对于最后一个视图,距离滚动视图的后沿为 10。

无需调整内容大小。没有布局相关的调用。只需添加约束即可。


在 cmets 中,您说您希望文本视图的宽度为滚动视图边界的三分之一。由于滚动视图约束的微妙之处(参见TN2154,滚动视图及其子视图之间的约束实际上是在滚动视图的contentSize 和子视图之间,而不是滚动视图的frame 之间),你不能说滚动视图的⅓。但是您可以说滚动视图所在视图的 ⅓,例如将上面的 200pt 宽度约束替换为:

textView.widthAnchor.constraint(equalTo: scrollViewContainer.widthAnchor, multiplier: 1.0 / 3.0, constant: -13),

同样,您说您希望它是滚动视图边界的高度小于 10(大概在两侧)。所以你可以做类似的事情,再次将 200pt 固定高度约束替换为:

textView.heightAnchor.constraint(equalTo: scrollViewContainer.heightAnchor, constant: -20)

现在,因为你不能引用滚动视图的bounds,所以我将滚动视图放在容器视图(scrollViewContainer)中,所以这个父视图的边界与滚动视图。现在约束可以引用这个父视图,您将设置这些子视图相对于滚动视图的边界,而不是滚动视图的contentSize

但是当你这样做时,你会在滚动视图中看到前三个视图(我可以向右滚动查看其余部分):

【讨论】:

  • 非常有效的方法..非常感谢先生!!
  • 当旋转到横向时它没有得到调整,我不想高度滚动,只有水平滚动......我正在设置 textview 高度,如下所示.. override func didRotate(来自fromInterfaceOrientation:UIInterfaceOrientation ) { scrollHeight = Int(self.scrollObj.bounds.size.height - 20) scrollWidth = Int(self.scrollObj.bounds.size.width / 3 - 10) self.view.layoutIfNeeded() self.scrollObj.layoutIfNeeded() }
  • 200x200 给定的检查视图不会出现,我希望我的 textview 大小从 10 像素开始,底部从 10 像素开始,文本视图宽度基于滚动视图宽度/3
  • 我想要基于滚动视图宽度的三列宽度
  • 挑战在于,在约束中,滚动视图及其子视图之间的约束中的引用指向滚动视图的contentSize,而不是滚动视图的frame/bounds。所以你必须参考滚动视图的父视图。但是一旦你这样做了,你就可以在设备旋转时自动调整 height-20 和 width/3-10 (或其排列)。请参阅上面的修订答案。
【解决方案2】:

在我的演示中我已经这样做了,

ViewDidLoad

        // add scrollView
        let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
        self.view.addSubview(scrollView)

        // scrollview contraints

        scrollView.translatesAutoresizingMaskIntoConstraints = false

        scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
        scrollView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0).isActive = true
        scrollView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0).isActive = true


        // add viewContainer
        let viewContainer = UIView(frame: CGRect(x: 0, y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height)) // View just after the SV
        viewContainer.backgroundColor = UIColor.lightGray
        scrollView.addSubview(viewContainer)

        // viewcontainer contraints

        //viewContainer.translatesAutoresizingMaskIntoConstraints = false

        viewContainer.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
        viewContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
        viewContainer.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
        viewContainer.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true


        // add different subview in scrollview

        var originY:Int = 0
        for  i in 0 ..< 5 {

            let myInnerView = UIView(frame: CGRect(x: 0, y: originY , width: Int(scrollView.frame.size.width), height: 100))
            myInnerView.translatesAutoresizingMaskIntoConstraints = false

            viewContainer.addSubview(myInnerView)


            // for different UI different color
            switch i {
            case 0:
                     myInnerView.backgroundColor = UIColor.green
                break
            case 1:
                myInnerView.backgroundColor = UIColor.purple
                break
            case 2:
                myInnerView.backgroundColor = UIColor.yellow
                break
            case 3:
                myInnerView.backgroundColor = UIColor.orange
                break
            case 4:
                myInnerView.backgroundColor = UIColor.blue
                break

            default: break

            }

            originY = originY+100+20
        }

添加此方法

override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
     self.view.layoutIfNeeded()

}

输出:

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2015-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多