Search code examples
iosswiftuiviewuiviewcontrolleruiview-hierarchy

UIView's frame gets overwritten randomly


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!


Solution

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