MichiganLabs / AnimatingTableViewHeader

A Swift project explaining how to animate a header above a UITableView
164 stars 35 forks source link

Getting the header to not expand until at the top of the TableView #4

Closed kylebrowning closed 7 years ago

kylebrowning commented 7 years ago

ANy ideas on how to make it so that when you scroll to the top, you dont expand the header until you get closer to the top, and the content comes out from under the header?

kylebrowning commented 7 years ago

Figured this out.

escully27 commented 7 years ago

How did you do it?

kylebrowning commented 7 years ago

@escully27 Sorry misunderstood your question.

I did it like this.

First set a scrolling position.

      if isScrollingDown {
            currentScrollingDirection = ScrollingDirection.down
            self.navigationController?.setNavigationBarHidden(true, animated: true)
        } else if isScrollingUp {
            currentScrollingDirection = ScrollingDirection.up
        }

Then,

        if scrollView.contentOffset.y <= 20 {
            self.navigationController?.setNavigationBarHidden(false, animated: true)
        }

        if canAnimateHeader(scrollView) {
            self.previousScrollOffset = scrollView.contentOffset.y

            // Dont do anything if were not above a certain height and we are scrolling up
            if scrollView.contentOffset.y >= 120 && currentScrollingDirection == ScrollingDirection.up {
                return
            }

            // Calculate new header height
            let newHeight = self.getNewConstraintValue(constraint: self.extendedNavBarHeightConstraint, valueMin: self.minHeaderHeight, valueMax: self.maxHeaderHeight, scrollDiff: scrollDiff)

            // Header needs to animate
            if newHeight != self.extendedNavBarHeightConstraint.constant {
                self.extendedNavBarHeightConstraint.constant = newHeight
                self.setScrollPosition(self.previousScrollOffset)
            }

            self.emailLabelWidthConstraint.constant = self.getNewConstraintValue(constraint: self.emailLabelWidthConstraint, valueMin: self.minEmailWidth, valueMax: self.maxEmailWidth, scrollDiff: scrollDiff)
            self.emailLabelHeightConstraint.constant = self.getNewConstraintValue(constraint: self.emailLabelHeightConstraint, valueMin: self.minEmailHeight, valueMax: self.maxEmailHeight, scrollDiff: scrollDiff)
            self.imageViewWidthConstraint.constant = self.getNewConstraintValue(constraint: self.imageViewWidthConstraint, valueMin: self.minImageViewWidth, valueMax: self.maxImageViewWidth, scrollDiff: scrollDiff)
            self.imageViewHeightConstraint.constant = self.getNewConstraintValue(constraint: self.imageViewHeightConstraint, valueMin: self.minImageViewHeight, valueMax: self.maxImageViewHeight, scrollDiff: scrollDiff)
            self.supportTopConstraint.constant = self.getNewConstraintValue(constraint: self.supportTopConstraint, valueMin: self.minSupportTop, valueMax: self.maxSupportTop, scrollDiff: scrollDiff)
            self.emailTopConstraint.constant = self.getNewConstraintValue(constraint: self.emailTopConstraint, valueMin: self.minEmailTop, valueMax: self.maxEmailTop, scrollDiff: scrollDiff)
        }
kylebrowning commented 7 years ago

I wrote a utilty function as well called getNewConstraintValue

    func getNewConstraintValue(constraint: NSLayoutConstraint, valueMin: CGFloat, valueMax: CGFloat, scrollDiff: CGFloat) -> CGFloat {
        switch currentScrollingDirection {
        case .down:
            return max(valueMin, constraint.constant - abs(scrollDiff))
        case .up:
            return min(valueMax, constraint.constant + abs(scrollDiff))
        case .none:
            return constraint.constant
        }
    }
escully27 commented 6 years ago

Thanks for the response. Not working for me.. I have no navigation bar involved so it's behaving in a worse way than original. Cheers

lordcodes commented 6 years ago

I needed it to work in this way and used a different solution.

In the canAnimateHeader method, I just checked if current scroll position is greater than the point you want it to remain collapsed. If it is then just do nothing at all, if it isn't then, animate the header.

private func canAnimateHeader(forScrollView scrollView: UIScrollView) -> Bool {
        if scrollView.contentOffset.y >= alwaysCollapsedScrollOffset {
            return false
        }
        // isContentToScroll is same as previous implementation, 
        // I just broke it up a little
        return isContentToScroll(forScrollView: scrollView)
    }

    private func isContentToScroll(forScrollView scrollView: UIScrollView) -> Bool {
        let headerSize = headerHeight - headerHeightMinimum
        let scrollViewMaxHeight = scrollView.frame.height + headerSize
        return scrollView.contentSize.height > scrollViewMaxHeight
    }