artexhibit
artexhibit

Reputation: 319

How to scroll to the View Controller top and display a Large Title?

I have a ViewControllerOne with a tableView constrained to a superview and filled with a content. User can scroll down some content, then switch to ViewControllerTwo and change tableView data source content on another.

When that happens and user returns to the ViewControllerOne I want the VC to be reset on its initial state at the top with a Large Title and a new content, but with a workaround I found it scrolls only till the tableView top and stops on a Small Title.

Here is the code:

  1. When user picks a new Data Source in ViewControllerTwo I save it as a bool in UserDefaults:

      UserDefaults.standard.set(true, forKey: "newDataSourcePicked")
    
  2. In ViewControllerOne I trigger the scrolling method in a viewWillAppear():

       override func viewWillAppear(_ animated: Bool) {
          super.viewWillAppear(animated)
           scrollVCUp()
       }
    
  3. Here is scrollVCUp(). Here I use the saved bool. Also use delay because its not scrolling without it:

     func scrollVCUp() {
     if newDataSourcePicked {
         traitCollection.verticalSizeClass == .compact ? setVCOffset(with: view.safeAreaInsets.top, and: updateLabelTopInset, delayValue: 0.1) : setVCOffset(with: biggestTopSafeAreaInset, and: updateLabelTopInset, delayValue: 0.1)
         UserDefaults.standard.set(false, forKey: "newDataSourcePicked")
     }
    }
    
  4. Here is setVCOffset():

     func setVCOffset(with viewInset: CGFloat, and labelInset: CGFloat, delayValue: Double = 0.0) {
        let firstVC = navigationController?.viewControllers.first as? CurrencyViewController
        guard let scrollView = firstVC?.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView else { return }
    
     if delayValue > 0.0 {
         DispatchQueue.main.asyncAfter(deadline: .now() + delayValue) {
             scrollView.setContentOffset(CGPoint(x: 0, y: -(viewInset - labelInset)), animated: true)
         }
     } else {
         scrollView.setContentOffset(CGPoint(x: 0, y: -(viewInset - labelInset)), animated: true)
     }
     }
    

I also have a tabBar and when I use the same code to scroll ViewControllerOne by tapping on a tabBar it scrolls and shows a Large Title, but doesn't work if we switch to another VC and back.

Here is a gif:

What should I do to scroll and always show a Large Title?

Upvotes: 2

Views: 950

Answers (1)

SchmidtFx
SchmidtFx

Reputation: 626

I found two possible approaches:

Approach 1

Don't use the same UIViewController instance, that holds the UITableView. Create a new one. (Your case: when ViewControllerOne push ViewControllerTwo). With this approach you get the "fresh" layout with large title every time you push the VC.

Approach 2

Scroll by calculating the UITableView.contentOffset. Use for that adjustedContentInset.top and round the value. With this approach you get the same result like approach 1, but with a visible back scrolling animation.

class ViewControllerTwo {

    private var _adjustedContentInsetTopRounded: CGFloat?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if let y = _adjustedContentInsetTopRounded {
            DispatchQueue.main.async {
                self.tableView.setContentOffset(
                    CGPoint(
                        x: 0,
                        y: -y
                    ),
                    animated: true
                )
            }
        }
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        _adjustedContentInsetTopRounded = tableView.adjustedContentInset.top.rounded(.up)
    }
}

Upvotes: 1

Related Questions