0

I'm having a problem with the way UIViews are set up.

Situation

I have a view that I'll add some detail views to. Those detail views are in sequential order, much like the rows of a UITableView are. This is actually a pretty good comparison. The difference is that in this view we're not scrolling up and down but sideways.

Of those detail views, there's always only one on the screen. When I add that first screen, I also load the next one (to the right) and draw it off screen (again, to the right.) When the user hits a button we precede to the next view with a small animation. The last visible view slides out to the left and from the right the second view comes in. At that moment, I keep a reference to the now left one and load a new right one, if there are any. I created a protocol makes any other object the data source for what I call the SlidableDetailViewController (SDVC).

Currently, I configure the detail views during the data source's didSet. During this setup, it generates those three frames (again, the left, center, and right ones).

Issue

When the view loads and first appears on the screen, the frame of the center view is not what it should be, but way smaller. The second detail view will be displayed correctly. When I go back to the first one, the frame will be updated and correct as well.

When I debug into it, the frames are calculated and set correctly. When I break in viewWillAppear(_:), even the view's frame is what I expect. When I do the same in viewDidAppear(_:) it already got shrunk down to this weird size nobody wanted. I'm not doing anything in those; I just used them to debug into the code here.

Does anyone see what I'm doing wrong here?

I'm pretty sure this is just stupid error on my end, but I can't figure it out right now.

Update for code examples

Calculating the frames:

private func calculateFrames() {
    // Get the size for our views from the dataSource.
    let detailViewSize: CGSize
    if let theDataSource = dataSource {
        detailViewSize = theDataSource.detailViewSize()
    } else {
        // Just use a few default values.
        detailViewSize = CGSizeMake(320, 185)
    }

    // Calculate the frame for on screen display.
    let detailFrameX = (view.frame.width - detailViewSize.width) / 2
    let detailFrameY = (view.frame.height / 2 - detailViewSize.height / 2)
    centerFrame = CGRectMake(detailFrameX, detailFrameY, detailViewSize.width, detailViewSize.height)

    // The other two frames are basically just offsets of the original centerFrame.
    leftFrame = centerFrame?.offsetBy(dx: (detailViewOffset * -1), dy: 0.0)
    rightFrame = centerFrame?.offsetBy(dx: detailViewOffset, dy: 0.0)
}

Loading the next view (loading the previous view works pretty much the same way):

func showNextDetailView() {
    // 1. If we're at the last item, we can't advance.
    if selectedIndex == detailsCount() - 1 {
        return
    }

    // 2. Check if there is a view on the left spot
    if leftView != nil {
        // 2.1. … if so, remove it from superView.
        leftView?.removeFromSuperview()
    }

    UIView.animateWithDuration(animationDuration, delay: 0.0, options: .CurveEaseInOut, animations: { () -> Void in
        // 3. Set the frame of the view at selectedIndex to leftFrame.
        self.centerView?.frame = self.leftFrame!
        // 4. Set the frame of the view at selectedIndex + 1 to center frame.
        self.rightView?.frame = self.centerFrame!
        }) { (finished) -> Void in
            print("animation to next detail view finished")
    }

    // 5. Increase the selectedIndex
    selectedIndex++

    // Store the new positions for quick reference.
    if let theDataSource = dataSource {
        if let newLeftView = theDataSource.detailViewAtIndex(selectedIndex - 1) {
            leftView = newLeftView
        }

        if let newCenterView = theDataSource.detailViewAtIndex(selectedIndex) {
            centerView = newCenterView
        }

        // 6. Check if we have more views left
        if detailsCount() - 1 > selectedIndex {
            // 6.1 … and if so, add a new one to the right.
            rightView = theDataSource.detailViewAtIndex(selectedIndex + 1)
            rightView?.frame = rightFrame!
            contentView.addSubview(rightView!)
        } else {
            rightView = nil
        }
    }
}

Thank you!

flohei
  • 5,248
  • 10
  • 36
  • 61
  • 1
    Could you please provide an example of the code your using to set up these frames and move between them? Also, you could use a collection view with a standard flow layout for side scrolling views like that, if it makes sense for you to do so. – Christopher Kevin Howell Dec 10 '15 at 14:21
  • Sure, please see the updated post. The idea to use a `UICollectionView` actually never crossed my mind as I was thinking of them as basically a grid view that does not lend it self to use it in a linear, left-to-right fashion. If it does, this might be a great way to get rid of a lot of my code. I have to look into this. Thanks for that hint! – flohei Dec 10 '15 at 14:36
  • 1
    Thanks, and yep, UICollectionViewFlowLayout literally fits a linear, left to right scrolling views. You can set the scroll direction on the layout to be .Horizontal. Might save you a bit of effort and reusable views are always good! – Christopher Kevin Howell Dec 10 '15 at 14:40
  • 1
    You could also use a `UIPageViewController` for this. Anyway, how are you creating the detail views? Are you loading them from a storyboard? Do they have autolayout constraints? – rob mayoff Dec 10 '15 at 14:50
  • Thanks for both your suggestions! The detail views use a xib which is set up using Auto Layout, yes. I tried to make the animations work with just pure Auto Layout, but that made me pull out my hair and so I went back to good old frames for the animations. – flohei Dec 10 '15 at 15:07
  • @robmayoff I didn't use the `UIPageViewController` in the end, but this led me in the right direction. A `UIScrollView` configured for horizontal paging works just fine. Together with UIViewController Containment, I can use it basically anywhere I want. So, if you want me to accept your answer please post it again as a response. Thanks again! – flohei Dec 16 '15 at 13:31

1 Answers1

0

I was able to fix this using the ordinary UIPageViewController as suggested by rob mayoff.

flohei
  • 5,248
  • 10
  • 36
  • 61