How to place N number of views Horizontally in scrollview using auto layout programmatically?

时光总嘲笑我的痴心妄想 提交于 2020-01-05 07:13:46

问题


I want to create N number of views in horizontally in scrollview. so I have created One Temporary View and i assigned current view to that.Then i place constraints based on that.Only one view showing others not showing. I want Horizontal space between two views. I am using VFL to set a auto layout. Here is my trying code.

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()
    }

This is what my design:-


回答1:


If you want to use constraints, you could do something like this:

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

Conceptually it's similar to what you have: So, for every text view, set the width to 200, height to 200, space to the prior view to 10. For the first view, it's 10 from the leading edge of the scroll view and for the last view, it's 10 from the trailing edge of the scroll view.

No content size adjustment is needed. No layout related calls. Just add the constraints and you're done.


In comments, you say you want the width of the text view to be one third of the scroll view's bounds. Because of the subtleties of constraints with scroll views (see TN2154, where constraints between a scroll view and its subviews are really between the contentSize of the scroll view and the subview, not the frame of the scroll view), you can't say ⅓ of the scroll view. But you can say ⅓ of the view the scroll view is in, e.g. replace the 200pt width constraint above with:

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

Likewise, you said you wanted it to be the height of the scroll view bounds less 10 (presumably on both sides). So you could do something like, again replacing the 200pt fixed height constraint with:

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

Now, because you cannot refer to the bounds of the scrollview, what I did was I put the scroll view inside a container view (scrollViewContainer), so the bounds of this parent view are the same as that of the scroll view. Now the constraints can refer to this parent view and you'll set these subviews relative to the bounds of the scroll view rather than the contentSize of the scroll view.

But when you do that, you see the first three views within the scroll view (and I can scroll to the right to see the rest):




回答2:


In my demo I have done this,

In 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
        }

Add this method

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

}

Output:



来源:https://stackoverflow.com/questions/41477626/how-to-place-n-number-of-views-horizontally-in-scrollview-using-auto-layout-prog

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!