Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

ios - Adding a view to a scroll view that will stretch to fill available width

I am using Auto Layout in a storyboard, no code, and am having difficulties in getting the view inside the scroll view's content view to stretch to fill the device width. I understand the issue is an ambiguous scroll view width, but I'm not sure how to make it non-ambiguous when I want it to always stretch to fill available width (with some padding).

In a view controller, I added a scroll view with 4 constraints: top, bottom, leading, trailing to superview. I added a view to the scroll view that will act as the content view - all subviews will be added to the content view. It has 4 constraints: top, bottom, leading, trailing to scroll view. I then added the view I want to be visible (a simple red box that has a fixed height but stretches to fill the screen width) to the content view. Its constraints are: trailing to superview (15), leading to superview (15), top to superview (15), bottom to superview (15), and height equals 60.

This results in an ambiguous scroll view width, and the frames are misplaced - it wants to set the box view's width to 0.

How can I set this up so the box view stretches to fill the device screen, resolving the scroll view content size width ambiguity?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In iOS 11 and later, scroll views have two sets of layout guides, one for the scrollable content, contentLayoutGuide (which dictates the scrolling behavior), and one for its frame, frameLayoutGuide (which dictates the size of the subview).

For example, this adds a subview that is inset by 20 points all the way around, whose width is set relative to the frame of the scroll view, but has a fixed height:

let subview = UIView()
subview.translatesAutoresizingMaskIntoConstraints = false
subview.backgroundColor = .red
scrollView.addSubview(subview)

NSLayoutConstraint.activate([
    subview.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor, constant: 20),
    subview.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: -20),
    subview.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor, constant: 20),
    subview.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor, constant: -20),

    subview.heightAnchor.constraint(equalToConstant: 1000),
    subview.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: -40),
])

You can also do this in IB, without any coding at all:

enter image description here

Prior to iOS 11, in the absence of the frameLayoutGuide, you had to set the subview’s constraints based upon the scroll view’s superview, and I outline that process below. But since iOS 11, the above is the more intuitive solution.


The constraints you described in your question are equivalent to the following VFL:

  • The scroll view occupies the entire view

    H:|[scrollView]|
    V:|[scrollView]|
    
  • The red view is 60 pt tall and has a margin of 15 to the edges of the scroll view's contentSize:

    H:|-(15)-[redView]-(15)-|
    V:|-(15)-[redView(60)]-(15)-|
    

The red view is ambiguous because there is nothing that defines its width. (The horizontal constraints between the red view and the scroll view define the scroll view's contentSize, not the red view's width. See Apple Technical Note 2154.)

You resolve this ambiguity by adding a constraint that says that the red view is 30pt narrower than the main view. So add constraint between red view an the scroll view's superview by control-dragging from the red view to the scroll view (and this is probably easiest to do from the document outline):

control drag

Then choose "equal widths":

equal widths

Having defined the red view to be the same width of the main view, you now have to alter that constraint to modify the constant to adjust for the margins you supplied between the red view and the scroll view's contentSize. Thus, select that constraint you just added and edit it, changing the constant to -30:

minus 30

Frankly, that Interface Builder technique is a little cumbersome. It may be easier to illustrate how to do this programmatically:

view.addConstraint(NSLayoutConstraint(item: redView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1.0, constant: -30))

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...