Code Monkey home page Code Monkey logo

scalingheaderscrollview's Introduction

     

Scaling Header Scroll View

A scroll view with a sticky header which shrinks as you scroll. Written with SwiftUI.

Read Article »

SPM Compatible Cocoapods Compatible Carthage Compatible License: MIT

Usage

  1. Put your header and content bodies code into a ScalingHeaderScrollView constructor.
  2. Set the necessary modifiers, see below.
struct ContentView: View {

    var body: some View {
       ScalingHeaderScrollView {
            ZStack {
                Rectangle()
                    .fill(.gray.opacity(0.15))
                Image("header")
            }
        } content: {
            Text("↓ Pull to refresh ↓")
                .multilineTextAlignment(.center)
                .padding()
        }
    }
}

Required parameters

header - @ViewBuilder for your header
content - @ViewBuilder for your content

Available modifiers, optional

passes current collapse progress value into progress binding: 0 for not collapsed at all, 1 - for fully collapsed

.collapseProgress(_ progress: Binding<CGFloat>)

allows to set up callback for ScrollView reaching the bottom

.scrollViewDidReachBottom(perform: @escaping () -> Void)

allows to set up callback and isLoading state for pull-to-refresh action

.pullToRefresh(isLoading: Binding<Bool>, perform: @escaping () -> Void)

allows to set up callback and isLoading state for pull-to-load-more action

.pullToLoadMore(isLoading: Binding<Bool>, perform: @escaping () -> Void)

allows content scroll reset, need to change Binding to true

.scrollToTop(resetScroll: Binding<Bool>)

allows to change current header height, need to change state, possible values are .collapsed, .expanded or .custom(CFGloat)

.snapHeaderToState(state: Binding<SnapHeaderState?>, animated: Bool)

changes min and max heights of Header, default min = 150.0 and max = 350.0

.height(min: CGFloat = 150.0, max: CGFloat = 350.0)

when scrolling up - switch between actual header collapse and simply moving it up (by default moving up)

.allowsHeaderCollapse()

when scrolling down - enable (disabled by default) header scale

.allowsHeaderGrowth()

Enable/disable (disabled by default) header snap. Available modes:

  • .disabled - Disable header snap.
  • .immediately - Once you lift your finger header snaps either to min or max height automatically.
  • .afterFinishAccelerating - At the end of scroll view deceleration the header snaps either to min or max height automatically.
.setHeaderSnapMode(.immediately)

Set custom positions for header snap (explained previous point). Specify any amount of values in 0...1 to set snapping points, 0 - fully collapsed header, 1 - fully expanded

.headerSnappingPositions(snapPositions: [CGFloat])

Set custom initial position to which scroll view will be automatically snapped to. Specify a value in 0...1, 0 - fully collapsed header, 1 - fully expanded

.initialSnapPosition(initialSnapPosition: CGFloat)

hide scroll indicators (false by default)

.hideScrollIndicators()

Examples

To try ScalingHeaderScrollView examples:

  • Clone the repo https://github.com/exyte/ScalingHeaderScrollView.git
  • Open terminal and run cd <ScalingHeaderScrollViewRepo>/Example/
  • Run pod install to install all dependencies
  • Run open Example.xcworkspace/ to open project in the Xcode
  • Try it!

Installation

dependencies: [
    .package(url: "https://github.com/exyte/ScalingHeaderScrollView.git")
]

To install ScalingHeaderScrollView, simply add the following line to your Podfile:

pod 'ScalingHeaderScrollView'

To integrate ScalingHeaderScrollView into your Xcode project using Carthage, specify it in your Cartfile

github "Exyte/ScalingHeaderScrollView"

Requirements

  • iOS 14+
  • Xcode 12+

Our other open source SwiftUI libraries

PopupView - Toasts and popups library
Grid - The most powerful Grid container
AnimatedTabBar - A tabbar with number of preset animations
MediaPicker - Customizable media picker
Chat - Chat UI framework with fully customizable message cells, input view, and a built-in media picker
ConcentricOnboarding - Animated onboarding flow
FloatingButton - Floating button menu
ActivityIndicatorView - A number of animated loading indicators
ProgressIndicatorView - A number of animated progress indicators
SVGView - SVG parser
LiquidSwipe - Liquid navigation animation

scalingheaderscrollview's People

Contributors

alex-m-b95 avatar andrewsava25 avatar andriizakhliupanyi avatar dcamenisch avatar eklipse2k8 avatar f3dm76 avatar kristalev avatar mnndnl avatar villygreen avatar zapletnev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

scalingheaderscrollview's Issues

Issues when presented as .pageSheet

Hi all,

Neat library, thanks for building it. I found some issues with the implementation. If you have a UIKit based app and want to present one of the examples inside of a nav with a modelPresentationStyle of .pageSheet with code like this:

 @objc func presentOnboarding() {
        let onboardingViewController = UIHostingController(rootView: OnboardingView())
        let nav = UINavigationController(rootViewController: onboardingViewController)
        nav.modalPresentationStyle = .pageSheet

        if let sheet = nav.sheetPresentationController {
            sheet.detents = [.medium(), .large()]
        }
        present(nav, animated: true, completion: nil)
    }

You get this result (this is based off your ColorScalingHeader example).
Simulator Screen Recording - iPhone 13 - 2022-06-08 at 10 19 46

Let me know if I can help further.

Is there "detect scrollview has reach bottom" capability?

Hi, firstly thank you for the great and helpful package.
I've worked a little time with the package.
In my use case, I need to detect whether the scroll view scrolled has reached bottom or not.
Is there is any function or work around to get the capability "detect scrollview has reach bottom"?

Thank you in advance. Have a great day.

I can't install using XCode Package or CocoPods

Says no compatible version found, there is no ver 1.0.0 in xcode package manager, and cocopods throw this error even without specifying a version: CocoaPods could not find compatible versions for pod "ScalingHeaderScrollView":
In Podfile:
ScalingHeaderScrollView

Using tabview inside content block

Hi
I am using tabiew with three views inside the ScallingHeaderScrollview content block, there is a height issue for the TabView views. The data is getting cut. Can't we provide dynamic height for the Tabview so that the complete data get scrolled and be visible.

Odd offset when displayed as a modal

Not sure if this is a bug or a known tradeoff, but is there a way to adjust the offset of the header when the screen is being displayed as a popup? Wanting to allow users to drag the screen down. Have tried a few different things but can't quite crack it

IMG_2597

I have a show stopper from using this :( please kindly help, project attached. Not working correctly with TabView

Hi, I've been looking forward to using this in a project for sometime until I faced an issue when embedding a nice TabView component.

The problem:
The TabView contains 3 views. Each view has a different number of rows so obvoiusly different content height for each view. The Tabview is not displaying the full content because it needs each view to be wrapped with a ScrollView of its own which will clash with the Card scrollView upon scrolling :(.

I thought as an alternative, I would try to calculate the height dynamically of each view to set the height of the TabView whenever the user switches the index but I am unable to get it right. and also if I do that it messes with the slide transition.

I am not sure what the best solution is I hope there is a way or somehow when we scroll the scrollview of each view to also scroll the card ! I am out of attempts at this stage. hoping someone could help with a solution.

I've attached the sample project just navigate to the Banking Screen you'll see the code there and a few extra things.
ScalingHeaderScrollView.zip

Simulator Screen Shot - iPhone 14 Pro Max - 2022-10-12 at 11 15 13

Header Image & Modal Sheet

Hey folks, I tried out the library in my demo project but hit a small bump. In the content section, I added a list of items. The header has a weird behavior when I add a modal sheet. :( This is also reproducible in the ProfileScreen example. I replaced the ContentView body with ProfileScreen() to display a sheet when clicking the hire button. Note: The longer the list, the lower the image.

Would you happen to know how to fix this?

ModalSheet_Bug.mov

Bug: Unresponsive Tap Area in Shrunk Header

I have encountered a bug in the "Scaling Header Scroll View". When the header is in its shrunk state, there is an unresponsive tap area in the region where the header would be expanded.
Please see attached video.

This issue can be temporarily resolved by disabling allowsHitTesting on the header, but this also allows interactions with buttons even when they are under the collapsed header, which is not the desired behavior.

Environment:
iOS version: 17.0.3
Xcode version: 15.0.1

ScalingHeaderScrollView.mov

Is it possible to programmatically scroll/hide the header?

Hello,

First off thank you for this package.

I am wondering if it is possible to programmatically scroll/hide the header? Currently, if my content height is long enough and is scrolled all the way down, after I toggle the binding value from .scrollToTop(resetScroll:) it smoothly moves to the top - the large header is hidden (screenshot 2).
Is there a way to achieve a similar behaviour, but when the large header is actually visible? If It is scrolled all the way up (screenshot 1) and I toggle the binding - nothing happens.
I basically want to move from screenshot 1 to screenshot 2 programmatically (in this case, after user selects a date on calendar) - is it possible?

Thank you

Doesn't work on iOS 17

Hello, the whole library doesn't work properly in iOS 17. I've tried more complex views and even sample code, collapse progress doesn't work at all.

Same code on iOS 16 and then iOS 17.
iOS 16 was on the simulator, iOS 17 I tried both the simulator and the real device

Scale header when scrolling up

Hi!

Could it be possible to display the header with max height when scrolling up at any point of the scrollview.

For example, filters on the headers that are hidden when scrolling down, but are shown when the user scrolls up a little bit.

Add option to set initial height.

I'm adding a view with a map in the header. The map would have a minimum value of about 80 and a maximum value of almost the entire screen. However, initially I'd like it to be somewhere in between. Right now the header always seems to default to the maximum height so it would be nice if it's possible to initially set the header height to something else than the maximum height.

Returning `nan`

https://github.com/exyte/ScalingHeaderScrollView/blob/6afd9b32821b42c9dacb351c8ad8ca49f723ef1d/Source/ScalingHeaderScrollView.swift#L351C5-L351C5

This line of code in some situations returns nan instead of 0 which is not good. I suggest add a condition like:

private func getCollapseProgress() -> CGFloat {
        let result = 1 - min(max((getHeightForHeaderView() - minHeight) / (maxHeight - minHeight), 0), 1)

        if result.isNaN {
            return 0
        } else {
            return result
        }
    }

or some another solution.

I haven't studied the code in depth but there is probably division by zero in some situation.

Add the ability to clip scroll view content

Thank you for this and other your amazing libraries!
May be you could help me to resolve one annoying issue: clipping/masking the content of a scroll view. I want that scroll view content has rounded corners and clipped to that shape. This might be even not related to your library, but so far I completely stuck. Would be appreciated any suggestions.
This is the effect I want to achieve (it's working in ios17 using .mask and .visualEffect modifiers), but I have to target ios 16, so I am looking for an alternative solution.
Video:
https://github.com/exyte/ScalingHeaderScrollView/assets/2717056/9c0a28db-b924-408b-af15-e94893d2fbfe
Photo:
Screenshot 2024-01-09 at 18 36 20

And this is the best I could achieve with your library.
Screenshot 2024-01-09 at 18 26 20

Thank you for making our work much easier with your libraries!

ProfileScreen example infer minHeight and maxHeight

Is it possible to infer the min and max height for the header without specifying it? I am trying to have a UIViewRepresentable as a large and small header but their height cannot be calculated for some reason and they are dynamic in nature.

Not Working in VStack

I have added a ScalingHeaderScrollView in VStack add added a customView with height 20 and ScalingHeader added spacing in content and crop the header. I have attached code and screen shot

Untitled

`
struct SimpleScalingHeader: View {

@Environment(\.presentationMode) var presentationMode

@State private var selectedImage: String = "image_1"
@State private var isLoading: Bool = false

var body: some View {
    VStack {
        HStack{
            Text("MyCustom View")
        }
            .frame(height: 20)
        ZStack(alignment: .topLeading) {
            ScalingHeaderScrollView {
                Image(selectedImage)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .clipped()
            } content: {
                Text(defaultDescription)
                    .padding()
            }
            .pullToRefresh(isLoading: $isLoading) {
                changeImage()
            }
            .allowsHeaderCollapse()
            .ignoresSafeArea()
            
            Button("", action: { self.presentationMode.wrappedValue.dismiss() })
                .buttonStyle(CircleButtonStyle(imageName: "arrow.backward"))
                .padding(.leading, 16)
        }
    }
}

// MARK: - Private

private func changeImage() {
    selectedImage = selectedImage == "image_1" ? "image_2" : "image_1"
    isLoading = false
}

}
`

Changes are done in Example. So please check

Same issue happen with TabView(Page)

`

import SwiftUI
import ScalingHeaderScrollView

struct SimpleScalingHeader: View {

@Environment(\.presentationMode) var presentationMode

@State private var selectedImage: String = "image_1"
@State private var isLoading: Bool = false

var body: some View {
    TabView {
        ZStack(alignment: .topLeading) {
            ScalingHeaderScrollView {
                Image(selectedImage)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .clipped()
            } content: {
                Text(defaultDescription)
                    .padding()
            }
            .pullToRefresh(isLoading: $isLoading) {
                changeImage()
            }
            .allowsHeaderCollapse()
            .ignoresSafeArea()
            
            Button("", action: { self.presentationMode.wrappedValue.dismiss() })
                .buttonStyle(CircleButtonStyle(imageName: "arrow.backward"))
                .padding(.leading, 16)
        }
    }
    .tabViewStyle(.page)
}

// MARK: - Private

private func changeImage() {
    selectedImage = selectedImage == "image_1" ? "image_2" : "image_1"
    isLoading = false
}

}
`

Please check it. ASAP

CPU usage 102%

Tools: Xcode Version 14.3.1, iPhone 14 Pro Simulator(iOS 16.4)

  • reproduce:
    Run the example app, navigate to the "Banking Screen"
  • Debug Log:
ForEach<Array<BankTransaction>, UUID, TransactionView>: the ID 49BB3E99-EF2E-4939-9097-361C36DE5B36 occurs multiple times within the collection, this will give undefined results!

Screenshot 2023-07-31 at 3 53 43 PM

Modifying state during view update, this will cause undefined behavior.

private func configure(scrollView: UIScrollView) {
        scrollView.delegate = scrollViewDelegate
        if let didPullToRefresh = didPullToRefresh {
            scrollViewDelegate.didPullToRefresh = {
                withAnimation { isLoading = true }
                didPullToRefresh()
            }
        }
        scrollViewDelegate.didScroll = {
            self.progress = getCollapseProgress() // ❗️ Modifying state during view update, this will cause undefined behavior.
        }
        scrollViewDelegate.didEndDragging = {
            isSpinning = false
            if !headerSnappingPositions.isEmpty {
                snapScrollPosition()
            }
        }
        DispatchQueue.main.async {
            if uiScrollView != scrollView {
                uiScrollView = scrollView
                snapInitialScrollPosition()
            }
        }
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.