LancelotKiin LancelotKiin - 4 months ago 29
Swift Question

Xcode/IB - Autolayout, multiple Containers View and Scroll View

I am trying to build an interface with the IB under Xcode with some Containers View.

I will try to do my best to explain the problem:

I have a main scene which contains two controllers. The first one, at the top of the scene, contains a "Last Post View", which retrieves the last post from a Wordpress website and displays the cover image, the post's date and the title.

The second one contains a Collection View which leads to other views.

Functionally and independently, everything seems to work fine. The problem is that I can not figure how to make work this "stack" with autolayout and fit on portrait and landscape modes and different devices.

Here is my Storyboard

enter image description here

The Home Controller's constraints

enter image description here

The Last Post View's constraints

enter image description here

The Collection View's constraints

enter image description here

..and finally, what I get

enter image description here

After hours of searching and attempts, I found that the Scroll View, contained in my Home Controller, must have only one direct child. But I don't know how to put the different constraints. Plus, I always get the message: Scrollable content size is ambiguous for "Scroll View".

Another problem that I have, is when I am in landscape mode, I can't scroll the "whole view". At best, I can scroll the Collection View only (when I can display it) but not the entire screen.

(Maybe it can help if I said that I am using Swift 2)

Does anyone have a suggestion? It will be much appreciated!

Many thanks!

EDIT 1

I tried to apply the Xingou's solution and I think I am quite close the goal but I obviously miss something.

Here is my HomeViewController

class HomeViewController: UIViewController {

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var containerViewHeightConstrait: NSLayoutConstraint!
@IBOutlet weak var lastPostContainerView: UIView!
@IBOutlet weak var scrollContainerView: UIView!
@IBOutlet weak var mainCollectionContainerView: UIView!

/*************************/
/***** VIEW DID LOAD *****/
/*************************/

override func viewDidLoad() {

super.viewDidLoad()

self.automaticallyAdjustsScrollViewInsets = false

self.lastPostContainerView.setNeedsDisplay()
self.mainCollectionContainerView.setNeedsDisplay()

self.containerViewHeightConstrait.constant = UIScreen.mainScreen().bounds.height - 64

//END viewDidLoad
}

...

/********************************/
/***** SET CONTAINER HEIGHT *****/
/********************************/

func needSetContainerHeight( height: CGFloat ) {

self.containerViewHeightConstrait.constant = height + lastPostContainerView.bounds.height + 200
self.scrollView.contentSize.height = height + lastPostContainerView.bounds.height + 200

print( "View Height Constrait \( self.containerViewHeightConstrait.constant )" )
print( "Scroll View Height \( self.scrollView.contentSize.height )" )

//END needSetContainerHeight
}

...


...and my MainCollectionController

class MainCollectionViewController: UICollectionViewController {

/*************************/
/***** VIEW DID LOAD *****/
/*************************/

override func viewDidLoad() {

super.viewDidLoad()

collectionView!.autoresizingMask = [ .FlexibleWidth ]

//END viewDidLoad
}

/****************************************/
/****************************************/

/***************************/
/***** VIEW DID APPEAR *****/
/***************************/

override func viewDidAppear( animated: Bool ) {

super.viewDidAppear( animated )
( self.parentViewController as! HomeViewController ).needSetContainerHeight( self.collectionView!.contentSize.height )

//END viewDidAppear
}

...


If I well understood what was proposed, here is how constraints should look like :

(Main) View

enter image description here

Scroll View

enter image description here

Subviews Container

enter image description here

Last Post Container

enter image description here

Collection Container

enter image description here

List of all constraints

enter image description here

... and what I get

Portrait

enter image description here

Landscape

enter image description here

I made a few tests with different extra constraints and I found out that I had to tell the Scroll View to fill its whole parent to display something on the screen (otherwise, I just get the red background).

Another thing is if I add a constraint for the space between Last Post Container and Collection Container, things are "well" positioned but I cannot click on the collection's cells anymore. But, if I don't add this constraint, things are more messy (for example, the collection view overlaps the post view), but I am able to click on the cells.

Unfortunately, the screen seems to be cropped and there are some differences when I rotate the screen. But I think I have to recompute the heights when the device is rotated (am I right?). The margin at the top of screen still here, but not always: it depends in which mode I started the app and how many rotation I do.

Another thing I forgot to mention, is the last post is asynchronously retrieved. So, the cover image, which has a variable size, is displayed after the main screen is displayed. I moved this in the AppDelegate - didFinishLaunchingWithOptions and stocked the post in the Notification Center but unfortunately, there's still some delay.

Any suggestions? :)

SOLUTION

Like Xingou said, it is far easier to use the header section of the collection view (Accessories / Section Header).

Answer

you need to solve the following thing:

  1. in you home sense, viewdidload , add the following code:

    self.automaticallyAdjustsScrollViewInsets = false this can make the red area between navagationbar and you picture disappear.

  2. fixed the Scrollable content size is ambiguous for "Scroll View": this is because the scrollview did not know the content size it would show。you need reset the constrain of the view( the two container view's super view ),i will call it scrollcontainerview:

scrollcontainerview.leading = scrollview.leading

scrollcontainerview.trailing = scrollview.trailing

scrollcontainerview.top = scrollview.top

scrollcontainerview.bottom = scrollview.bottom

scrollcontainerview.width = self.view.width

scrollcontainerview.height = 603 //we will chang this value through code

now the "the Scrollable content size is ambiguous for "Scroll View"" error should be disappeared.

  1. change the scrollcontainerview.height in code : in you home scene, drag the scrollcontainerview.height constraint into you view controller,and chang it to fit the screen:

    @IBOutlet weak var contianerViewHeightConstrait: NSLayoutConstraint!

     override func viewDidLoad() {
        super.viewDidLoad()
    
        self.automaticallyAdjustsScrollViewInsets = false
        //because we set the height at const 603,but height is different on different device,so we need chang it to the correct value
    
        contianerViewHeightConstrait.constant =       UIScreen.mainScreen().bounds.height - 64
    
    }
    

and now you can see the two container view fill the screen。

  1. need scroll the whole content, not only the collection view: to solve this problem ,you need assign a correct height to the scrollview.contentsize.height . in you collection view controller:
 override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    (self.parentViewController as!    HomeScenceViewController).needSetContainerHeight(self.collectionView.contentSize.height)
}

then add a method in HomeScenceViewController

 // set the containerview and scrollview height
    func needSetContainerHeight(height:CGFloat){
        //the 200 is your first container view's height
        contianerViewHeightConstrait.constant = height + 200
        scrollview.contentSize.height = height + 200
    }

and now you should can scroll the whole content

Comments