UIScrollView's origin changes after popping back to the UIViewController

后端 未结 17 2217
不知归路
不知归路 2020-12-07 16:50

I have a UIViewController subclass as a scene in the storyboard that contains a UIScrollView containing various subviews. One of the subviews is a

相关标签:
17条回答
  • 2020-12-07 16:53

    Actually, I put that line of code in viewDidDisappear, and so that it remembers the offset when the view reappears, I added this line before it

     self.contentOffset = self.scrollView.contentOffset;
    

    as well as

     - (void)viewDidLayoutSubviews { 
           self.scrollView.contentOffset = self.contentOffset; 
     }
    
    0 讨论(0)
  • 2020-12-07 16:53

    In iOS 11, I faced a similar issue where when I come back after popping a view controller, the tableview in the previous viewcontroller used to adjust its content inset automatically which resulted in tableview contents jumping from top abruptly. The following solution worked for me in iOS 11.

    In Storyboard, select the tableview and go to Attributes inspector and uncheck "Automatic" for Row Height and Estimate fields. Also change the content insets from "Automatic" to "Never".

    [Table view]

    0 讨论(0)
  • 2020-12-07 16:54

    Try this in viewWillAppear of the view controller you pop back into:

     self.scrollView.contentOffset = CGPointMake(0, 0);
    

    Edit: When also adding Peter's code you get the best results with:

    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:YES];
        self.scrollView.contentOffset = CGPointMake(0, 0);
    }
    

    plus

    - (void)viewWillDisappear:(BOOL)animated {  
        self.recentContentOffset = self.scrollView.contentOffset;
        [super viewWillDisappear:animated];
    }
    

    and

    - (void)viewDidLayoutSubviews {
        [super viewDidLayoutSubviews];
        self.scrollView.contentOffset = CGPointMake(0, self.recentContentOffset.y);
    }
    

    You return to the original scroll position, have no visual side-effect and the scroll view itself is correctly positioned in its superview (which was a problem) after coming back.

    0 讨论(0)
  • 2020-12-07 16:54

    I posted a question recently about a similar issue but in a different context: Frame doesn't reflect auto layout constraints after dismissing modal view controller

    In my case, the origin of a custom container view inside the scroll view is displaced, rather than the origin of the scroll view itself. With respect to auto layout, the custom container view is pinned to the four sides of the scroll view.

    The container view is created and configured programmatically rather than in IB. Although the constraints of the container view are unchanged, the container view's origin is displaced after dismissing a modal view controller. This disconnect between constraints and frames is inexplicable.

    What's even more remarkable is that the origin displacement problem remained after I removed and re-added all related constraints.

    I came up with a similar solution: save the value for the current content offset, set the content offset to "zero", let the system swap out your views, then restore the content offset (i.e., a content-offset dance).

    In my case, however, I had to take additional steps to resolve the issue. My scroll view is a paging scroll view that gets its subviews from a tilePages method (demoed in an old WWDC video). Changing the scroll view's content offset triggers the tilePages method via the UIScrollViewDelegate's scrollViewDidScroll: method. I had to set a flag to turn off tilePages while I did the content-offset dance, otherwise the app would crash.

    Hopefully, I can remove the code for the content-offset dance when I upgrade to iOS 7.

    0 讨论(0)
  • 2020-12-07 16:55

    After trying lots of things:

    • Set automaticallyAdjustsScrollViewInsets to false to see if the problem was with the navigation bar.
    • Playing with fixed cell heights...
    • Caching the cell heights...
    • Keeping a property with the table view offset, caching it and setting it on viewWillAppear...
    • etc.

    I realised the issue was about the estimatedRowHeight value, which was set to 50px, when it should be ~150px. Updating the value fixed the issue and now the table view keeps the same offset.

    0 讨论(0)
  • 2020-12-07 16:57

    None of the above worked for me, I managed to do my own custom Push/Pull animation instead and it works like a charm.

    First, add this class which implements Push scenario

    class PushAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    
        // get reference to our fromView, toView and the container view that we should perform the transition in
        let container = transitionContext.containerView
        let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
    
        // start the toView to the right of the screen
        var frame = toView.frame
        frame.origin.x = container.frame.width
        toView.frame = frame
    
        // add the both views to our view controller
        container.addSubview(toView)
        container.addSubview(fromView)
    
        // get the duration of the animation
        let duration = self.transitionDuration(using: transitionContext)
    
        // perform the animation!
        UIView.animate(withDuration: duration, animations: {
    
            var frame = fromView.frame
            frame.origin.x = -container.frame.width
            fromView.frame = frame
    
            toView.frame = container.bounds
    
        }, completion: { _ in
            // tell our transitionContext object that we've finished animating
            transitionContext.completeTransition(true)
    
        })
    }
    }
    

    Then add this class which implements pop scenario

    import Foundation
    import UIKit
    
    class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        // get reference to our fromView, toView and the container view that we should perform the transition in
        let container = transitionContext.containerView
        let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
    
        // set up from 2D transforms that we'll use in the animation
        let offScreenRight = CGAffineTransform(translationX: container.frame.width, y: 0)
    
        // start the toView to the right of the screen
        var frame = toView.frame
        frame.origin.x = -container.frame.width
        toView.frame = frame
    
        // add the both views to our view controller
        container.addSubview(toView)
        container.addSubview(fromView)
    
        // get the duration of the animation
        let duration = self.transitionDuration(using: transitionContext)
    
        // perform the animation!
        UIView.animate(withDuration: duration, animations: {
    
            fromView.transform = offScreenRight
            toView.frame = container.bounds
    
        }, completion: { _ in
            // tell our transitionContext object that we've finished animating
            transitionContext.completeTransition(true)
    
        })
    }
    }
    

    Then add this line to your viewDidLoad method to change default NavigationController Delegate

    self.navigationController?.delegate = self
    

    And see the magic :)

    0 讨论(0)
提交回复
热议问题