Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Auto-layout constraints and frames not working

I have a Scroll View nested inside a View Controller which is rested in a Container. The View Controller, which uses a specified class called ScrollingViewController looks like this:

class ScrollingViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView! //outlet for the Scroll View

    override func viewDidLoad() {
        super.viewDidLoad()

        // 1) Create the two views used in the swipe container view
        var storyboard = UIStoryboard(name: "App", bundle: nil)
        var subOne: SubProfileOneViewController = storyboard.instantiateViewControllerWithIdentifier("subone") as! SubProfileOneViewController
        var subTwo: SubProfileTwoViewController = storyboard.instantiateViewControllerWithIdentifier("subtwo") as! SubProfileTwoViewController

        // 2) Add in each view to the container view hierarchy
        //    Add them in opposite order since the view hierarchy is a stack
        self.addChildViewController(subTwo);
        self.scrollView!.addSubview(subTwo.view);
        subTwo.didMoveToParentViewController(self);

        self.addChildViewController(subOne);
        self.scrollView!.addSubview(subOne.view);
        subOne.didMoveToParentViewController(self);

        // 3) Set up the frames of the view controllers to align
        //    with each other inside the container view
        var adminFrame :CGRect = subOne.view.frame;
        adminFrame.origin.x = adminFrame.width;
        subTwo.view.frame = adminFrame;

        // 4) Finally set the size of the scroll view that contains the frames
        var scrollWidth: CGFloat  = 2 * self.view.frame.width
        var scrollHeight: CGFloat  = 262
        self.scrollView!.contentSize = CGSizeMake(scrollWidth, scrollHeight);
        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Basically what's happening is that two View Controllers that use that class SubProfileOneViewController and SubProfileTwoViewController, respectively, are being instantiated as subOne and subTwo. These are then added into the Scroll View as children in order to create an interface where the user can swipe right to access another view (almost like Snapchat). subOne and subTwo should be side by side and the user should be able to scroll from one to the next and vice versa.

Here's what this all looks like on my storyboard: enter image description here

SubProfileOneViewController and SubProfileTwoViewController each have a view (represented by green and red respectively) and each have the same exact constraints which are: Height = 262, Trailing space to superview = 0, Leading space to superview = 0, Top space to superview = 0

Ideally, when run, there should be two views, one green and one red and the user should be able to swipe between each one. However, here's what actually happens: enter image description here

The green and red views don't take up the entire screen width and instead are condensed into a small sliver on the left, and the majority of the View Controllers are white instead of their respective color. I've tried many things and I'm not sure what I'm doing wrong.

(credit for code in ScollingViewController goes to lbrendanl on github, linked here: https://github.com/lbrendanl/SwiftSwipeView)

like image 303
applemavs Avatar asked Sep 28 '22 03:09

applemavs


2 Answers

ScrollView with auto layout works differently either you can use only one subview by setting translatesAutoresizingMaskIntoConstraints = true and setting contentSize explicitly. Or you set translatesAutoresizingMaskIntoConstraints = false and let it find out constraint it self. Visit this link for more details

As you want both should take full screen then you need to add AutoLayoutConstraints, something like this

  1. Pin greenView left to super view
  2. Set greenView width to same as scrollViewWidth i.e bounds.size.width (not contetnSizeWidth)
  3. Pin redView left to greenView right
  4. set redView width value same as scrollViewWidth i.e bounds.size.width (not contetnSize.width)
like image 77
Adnan Aftab Avatar answered Oct 17 '22 02:10

Adnan Aftab


Note: I would recommend using an Autolayout solution, which will automatically handle orientation changes, screen sizes, all with much less code and more robust solution than what I develop below.

With the current approach, using embedded UIViewControllers, you must wait for these controllers to complete their respective initializations, which are in direct competition with your layout. Note that you will need to recalculate positions + sizes upon orientation changes. Again, while this works, it is not a sound design since it calls for a lot of code, logic, and general functionalities you would get for free using NSLayoutConstraint.

(tested, built, linked and ran):

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    // 3) Set up the frames of the view controllers to align
    //    with each other inside the container view
    var adminFrame = self.view.frame
    subOne.view.frame = adminFrame
    adminFrame.offset(dx: self.view.frame.width, dy: 0)
    subTwo.view.frame = adminFrame;

    // 4) Finally set the size of the scroll view that contains the frames
    let scrollWidth = 2 * self.view.frame.width
    self.scrollView!.contentSize = CGSizeMake(scrollWidth, self.view.frame.height)
}

Here is the scrolling (in action). Notice that I created views of class ViewLayoutAssistant.swift which allows you to visualize position + scale very conveniently.

enter image description here

You also do not need to tell the view its hierarchy has changed. This last bit of code should get you going:

@IBOutlet weak var scrollView: UIScrollView!
weak var subOne: SubProfileOneViewController!
weak var subTwo: SubProfileTwoViewController!

    self.addChildViewController(subTwo);
    self.scrollView!.addSubview(subTwo.view);
    self.addChildViewController(subOne);
    self.scrollView!.addSubview(subOne.view);
like image 29
SwiftArchitect Avatar answered Oct 17 '22 01:10

SwiftArchitect