Giter Site home page Giter Site logo

fermoya / swiftuipager Goto Github PK

View Code? Open in Web Editor NEW
1.2K 12.0 162.0 38.98 MB

Native Pager in SwiftUI

License: MIT License

Swift 97.00% Ruby 0.61% Objective-C 0.28% Shell 2.11%
swiftui ios macos tvos watchos swiftui-components swift5 pager pagerview cocoapods

swiftuipager's Introduction

SwiftUIPager

CI codecov Swift Package Manager compatible Cocoapods Carthage compatible CocoaPods platforms License: MIT

SwiftUIPager provides a Pager component built with SwiftUI native components. Pager is a view that renders a scrollable container to display a handful of pages. These pages are recycled on scroll, so you don't have to worry about memory issues. Pager will load just a handful of items, enough to beatifully scroll along.

Create vertical or horizontal pagers, align the cards, change the direction of the scroll, animate the pagination... Pager lets you do anything you want.

Example of usage

Requirements

  • iOS 13.0+
  • macOS 10.15+
  • watchOS 6.0+
  • tvOS 13.0+
  • Swift 5.1+

Installation

CocoaPods

pod 'SwiftUIPager'

Swift Package Manager

In Xcode:

Carthage

github "fermoya/SwiftUIPager"

Manually

  • Download SwiftUIPager.xcframework
  • Create a group Frameworks inside your project and drag and drop SwiftUIPager.xcframework

Manual Installation Step 1

  • Make sure in your target's build phases that the option Embed & Sign is selected:

Manual Installation Step 2

Known Issues

  • NavigationLink and Button might work oddly with Pager if pagingPriority(.simultaneous) is used in SwiftUI 1.0 and iOS 13. This issue isn't reproducible in iOS 14 beta. For more information, follow this link.
  • Depending on the Xcode version, you might run into a precondition failure affecting SwiftUI 1.0 and iOS 13. This issue doesn't occur on Xcode 12 beta. For more information about workarounds, see Precondition failure: invalid value type for attribute #60.

Feedback

If you happen to encounter any problem or you have any suggestion, please, don't hesitate to open an issue or reach out to me at [email protected].
This is an open source code project, so feel free to collaborate by raising a pull-request or sharing your feedback.

Support Open Source

If you love this library, understand all the effort it takes to maintain it and would like to support me, you can buy me a coffee by following this link:

Buy Me A Coffee

You can also sponsor me by hitting the GitHub Sponsor button. All help is very much appreciated.

License

SwiftUIPager is available under the MIT license. See the LICENSE file for more info.

swiftuipager's People

Contributors

bernhardwaidacher avatar csknns avatar elliottburris avatar fermoya avatar fermoyadrop avatar fred-bowker avatar geraldvoit avatar hugosay avatar imjameshall avatar jackyoustra avatar jhildensperger avatar knurldamer avatar lewis-smith avatar mouness2020 avatar nylki avatar p-larson avatar pierrecapo avatar rusik avatar soulfoodz avatar teameh 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

swiftuipager's Issues

[FEAT] Where is itemTappable option mentioned in the Medium article?

Is your feature request related to a problem? Please describe.

Hi! In the Medium article I saw a mention of a feature: itemTappable - makes the pages clickable. But I can't find it in the library itself. Can someone please clarify?

Also, I'd like to know whether this feature enables the user to tap on non-focused page in order to switch to that page, because I could really make use of that functionality.

Thanks.

Can not swipe throw pages when content on the bottom of the screen

Can not swipe throw pages when pager is on the bottom of the screen. It was working with 1.3.0 version, but dose not works with 1.3.1 version.

Code which places pages on the bottom of the screen:

    var body: some View {
        Pager(page: $model.selectedPage, data: model.playrooms) { playroom in
            VStack {
                Spacer()
                HStack {
                    Text("Page text on the botton which can not be moved.")
                }
                .background(Color.red)
            }
        }
        .onPageChanged({ self.model.mapViewModel.selectPinAt(index: $0) })
        .itemSpacing(2)
        .itemAspectRatio(0.8, alignment: .end)
    }

image

[QUESTION] How to add animation to my page?

Describe the bug
When I add an element that has an animation inside the Pager, it will not animate.

To Reproduce
Sample code:

struct HighlightButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .scaleEffect(configuration.isPressed ? 0.95 : 1)
    }
}

struct DemoView: View {
    @State var currentPage: Int = 0
    var items = Array(0..<4)
    
    var body: some View {
        Pager(page: $currentPage, data: items, id: \.self) { index in
            Button(action: {}) {
                ZStack(alignment: .topLeading) {
                    Color.blue
                    
                    VStack(alignment: .leading) {
                        Spacer()
                        
                        Text("Title")
                            .foregroundColor(.white)
                            .font(.system(size: 16, weight: .bold))
                        
                        Text("Subtitle")
                            .foregroundColor(.white)
                            .font(.system(size: 12, weight: .light))
                            .padding(.top, 2)
                    }
                    .padding(10)
                }
                .clipped()
                .foregroundColor(.white)
                .frame(width: 224, height: 317, alignment: .topLeading)
                .cornerRadius(7)
            }
            .shadow(radius: 4)
            .buttonStyle(HighlightButtonStyle())
        }
    }
}

Expected behavior
Pager should not cancel all animations for its content.

Environment:

  • iOS
  • Device: iPhone 11 Pro, simulator, previews
  • SwiftUIPager version 1.9.0

Additional context
I noticed that the issue is with ".animation(nil)" after opacity in body for PagerContent.swift. If I remove this modifier, the animations inside Pager's content work fine.

[FEAT] Support RandomAccessCollection in the initializers rather than fixed Array

Is your feature request related to a problem? Please describe.
There is currently no good way library out that supports infinitive page swiping with an unknown size. This could be very useful. Example use case: Infinitiv calendar swipe view. The issue is, a calendar does not have a clear end in both directions. First, when I found this library I thought I finally have a good way to implement a week calendar view. However, then I found out that I still need to pass an array with a known size and a positive index.

Describe the solution you'd like
A way to use this without passing an array. Instead of:

@State var page: Int = 0
var items = Array(0..<10)

var body: some View {
    Pager(page: $page,
          data: items,
          id: \.identifier,
          content: { index in
              // create a page based on the data passed
              Text("Page: \(index)")
     })
 }

this

@State var page: Int = 0

var body: some View {
    Pager(page: $page,
          content: { index in
              // create a page based on the data passed
              // Index goes in both directions and can be negative
              Text("Page: \(index)")
     })
 }

Describe alternatives you've considered

The alternative would be to allow to pass custom array implementations. For example: Some struct which implements the RandomAccessCollection. Then you could implement your own "array" which auto extends itself in both directions. Negative and positive. RandomAccessCollection supports this:

    override var startIndex: Self.Index { get }

    override var endIndex: Self.Index { get }

You can update and define your own end and start.

I tried to implement something similar once by myself. But this is a bit buggy and not really nice:
https://gist.github.com/TheNoim/6ca6f35eb830a2d53d6adf04d68e05ba

Infinite scrolling an array

Hi, is it possible to loop back over the array. I tried adjusting the $pageIndex in the onPageChanged modifier, if one reaches the end of the list loop back to the beginning or if scrolls towards the beginning loops to the back. But changing the $pageIndex creates a scroll to animation to the index which isn't seamless. Just wondering if you maybe know what could be done?

[BUG] crash when trying to reload views too quickly

Describe the bug
A clear and concise description of what the bug is.

My app crashes during the SizeViewModifier .onReload perform self.size = proxy.size
Logs: "Recorded stack frame" and "enqueued from com.apple.main-thread (Thread 1)"

To Reproduce

  • Steps to reproduce the behavior
  • Sample code

Works fine when I navigate away from a view that has pager and then back SLOWLY, but doing so quickly causes this crash.

Pager(page: self.$currentPage, data: Array(0..<self.postArray.count), id: \.self, content: { index in
                        PostView(
                            loading: self.$loading,
                            chosenIndex: self.carouselStartIndex,
                            post: self.postArray[index],
                            nested: self.nested,
                            parent: self,
                            postIndex: index,
                            receivedIndex: self.$currentPage)
                        
                    }) // pager
                        //                        .multiplePagination() // too buggy right now
                        .vertical()
                        .swipeInteractionArea(.page) // or else the post carousel can't be scrolled
                        .onPageChanged { newIndex in
                            if newIndex == 0 { self.counterCheck += 1 } else {
                                self.counterCheck = 0
                            }
                            self.currentPage = newIndex
                            
                            if !self.loadedOnce { self.loadedOnce = true }
                    } // page change`

Expected behavior
A clear and concise description of what you expected to happen.

To not crash

Screenshots / Videos
If applicable, add screenshots to help explain your problem.

Screen Shot 2020-07-28 at 5 08 00 PM

Environment:

  • OSX: [e.g. iOS] 13.5.1
  • Device [e.g. iPhone6] iPhone 11 Pro
  • SwiftUIPager version 1.9.0-beta.3

Additional context
Add any other context about the problem here.

[BUG] Custom pagingAnimation not working as expected?

Hi

When scrolling through my pages, i use the following animation:
.pagingAnimation { (_) -> PagingAnimation in return .custom(animation: .interactiveSpring()) }

When I use the following animation to switch between pages:
withAnimation(.interactiveSpring()) { self.currentIndex += 1 }

I don't get the same animation, any ideas how to fix this?

Originally posted by @VanHoutte in #67 (comment)

Populate from CoreData FetchRequest

Hi, couldn't see anywhere else to ask this.. just wondering if it's possible to use a fetch request result as the data for the pager?

I currently have:

@FetchRequest(entity: Order.entity(), sortDescriptors: [])

var orders: FetchedResults<Order>

I tried setting the Pager.data parameter to orders, but that didn't work.

List in Pager: Scrolling issue for vertical list in horizontal pager

I'm trying to achieve card view with list inside pager view. I'm facing scrolling issue because of gesture. swipeGesture is getting detected even if I scroll vertically in List.

Here is demo code:

struct ContentView: View {
    @State var pageIndex = 0
    var colors: [Color] = [
        .red, .blue, .black, .gray, .purple, .green, .orange, .pink, .yellow, .white
    ]
    
    var body: some View {
        VStack {
            Pager(page: self.$pageIndex, data: self.colors, id: \.self) { color in
                ZStack {
                    RoundedRectangle(cornerRadius: 8)
                        .foregroundColor(color)
                    VStack(alignment: .leading) {
                        Text("Today").padding()
                        List(0..<100) { num in
                            Text("\(num)")
                        }.padding()
                    }
                }
            }
            .itemSpacing(16)
            .padding(24)
            .interactive(0.95)
        }
    }
}

ezgif-6-d5c316130c84
Compare with App Store : ezgif-6-688945432a99

It is lagging in performance, not responding well when you scroll list at first time. Later it will work.

Scroll animation to sections

Hey, amazing paging component, I love it!

I'm still quite a SwiftUI newbie, so I have a question about animation...

@State var page: Int = 0

I want to be able to set my page index to the start of a different section of items.
For example, page is set to 0, the user pressed a button to go to the 2nd section of pages, so page will get set to 3 (if there are 3 pages per section).
At the moment, when I set page to 3, the view will update immediately to that state.

What might be the best way to support scrolling/animating the pager to the correct page?

Thanks!

Compatibility with iOS < 13 projects

Thanks for this component, it looks awesome! I wanted to try it out in my iOS 10+ project but unfortunatly that's not possible due to the iOS 13+ target.

I understand that SwiftUI needs iOS 13+, but I've got a legacy project where I use SwiftUI for some new features that are just disabled for iOS <= 12 users.

Would it be possible to lower the target and add @available(iOS 13, *) (and macOS / watchOS) markers to the code so it can be used in lower target projects as well?

[FEAT] Modifier to customize page sensitivity

Describe the bug
Scroll is inconsistent. At times requires larger swipe to trigger scroll to next page and other times even with a long swipe the scroll is not triggered.

To Reproduce
Normal implementation, did try with infinite loop or no loop.

Expected behavior
Increased sensitivity to swipe (lower swipe distance) or more consistency.

Screenshots / Videos
Don't quite need? This may have been already addressed and just requires the sensitivity modifier?

Environment:
iOS 14 and PadOS 14 and iOS 13.0
iPhone XS Max and iPad mini, iPad Pro 12.9" 3rd Gen
SwiftUIPager 1.9.0

Additional context
Didn't use to occur before. Did read in prior issues that a feature affected it and that v1.9.0 had it fixed, not sure if I need to add a modifier to the Pager or not.

If the pager view has padding and cornerRadius and use .interactive, the UI breaks

Pager(data: dataList) { data in
   VStack(alignment: .leading, spacing: 0) {
      Text(data.text).foregroundColor(.white)
    }
    .padding()
    .background(Color.red)
    .cornerRadius(4)    
}

Before scrolling between pages, everything looks fine. After any scroll, the text becomes covered by the background colour. If either padding or cornerRadius is removed, it works fine.

The pager don't have the expected height

issue:
The pager don't have the expected height.

seems like the view is clipped at this line.

complete code to reproduce the issue:

import SwiftUI
import SwiftUIPager

struct IssueView: View {
    @State var page: Int = 0
    
    @State var data = Array(0..<10)
    
    var body: some View {
        VStack(alignment: .leading){
            
            HStack {
                VStack(alignment: .leading) {
                    HStack {
                        Text("4").font(.title).foregroundColor(Color("defaultColor"))
                        Text("月")
                    }.padding(.bottom, 10)
                    Text("月份").padding(.leading, 10)
                        .padding(.trailing, 10)
                        .foregroundColor(Color.white)
                        .background(Color("defaultColor"))
                }.padding(.leading, 20).padding(.trailing, 10).fixedSize(horizontal: true, vertical: false)
                Divider()
                
                
                Pager(page: self.$page,
                      data: self.data,
                      id: \.self) {
                        self.pageView($0)
                }.fixedSize(horizontal: false, vertical: false)
                //                self.pageView(0)
            }.fixedSize(horizontal: false, vertical: true)
            Divider()
            List {
                Text("test")
            }.frame(maxHeight: .infinity)
        }.padding()
    }
    
    func pageView(_ page: Int) -> some View {
        HStack{
            IssueItemView().frame(maxWidth: .infinity)
            IssueItemView().frame(maxWidth: .infinity)
            IssueItemView().frame(maxWidth: .infinity)
            IssueItemView().frame(maxWidth: .infinity)
            IssueItemView().frame(maxWidth: .infinity)
            IssueItemView().frame(maxWidth: .infinity)
            IssueItemView().frame(maxWidth: .infinity)
        }.background(Color.yellow)
    }
}

struct IssueItemView: View {
    var body: some View {
        VStack(spacing: 10) {
            Text("日")
            Text("5")
            Text("行").background(Color.gray).foregroundColor(Color.white)
        }
    }
}

struct IssueView_Previews: PreviewProvider {
    static var previews: some View {
        IssueView()
    }
}

[REQUEST] multiplePagination should page based on scroll velocity

Describe the bug
Set page size instead of just the item size

To Reproduce
create a a Pager with small items that aren't full width. swiping assumes the items are the size of the screen

Expected behavior
Instead of just being able to set the item size, I should be able to set the page size, so swiping to the next item feels faster than if it were the entire page. It takes a lot of force on small items because the Pager always assumes that a page is close to the entire width of the device.

Video
RPReplay_Final1600805259.MP4.zip

Pager's scroll blocks the ScrollView's scroll

I've seen there was a similar issue (#3) but then it was closed and an entirely different use case with ScrollView was discussed, so I am going to post this.

Basically, if a Pager view is placed inside a ScrollView with vertical scroll, the former blocks the parent's scroll, unless the drag gesture starts outside the Pager's view area.

This is not an issue caused by Pager itself, but there might be solutions that would work if applied to the library. I haven't seen any perfect solutions yet though.

There are some discussions here: https://stackoverflow.com/questions/57700396/adding-a-drag-gesture-in-swiftui-to-a-view-inside-a-scrollview-blocks-the-scroll. The trick with the button (https://stackoverflow.com/a/59961959/1719285) can be applied outside the library, but this then blocks the tap gestures inside each view (in my case, the remove button). The only solution that worked more or less OK (ish) was to have DragGesture(minimumDistance: 30, coordinateSpace: .local) for the swipeGesture of the library.

Negative Index

Describe the bug
Pager seems to support negative pages? When I insert my content into the vertical scrolling pager, it seems that I am able to scroll up from index 0 to index -1. I am wondering how to get rid of this.

To Reproduce

  1. Pager appears and starts at index 0 as expected. All expected content is viewable and user can swipe through all of the positive index pages normally.
  2. However, at index 0, if the user swipes up, the index becomes -1 and they are unable to see any content.
  3. After that, the user might swipe up again, and the index returns to 0.
  4. The user could also swipe back down from index -1 to return to index 0.
  • Sample code: This is what I am currently using.
    Pager(page: self.$currentPage, data: Array(0..<self.postArray.count), id: \.self, content: { index in PostView( loading: self.$loading, postIndex: index, receivedIndex: self.$currentPage) }) // pager .contentLoadingPolicy(.lazy(recyclingRatio: 2)) // to help with lag .vertical() .swipeInteractionArea(.page) // or else the post carousel can't be scrolled .onPageChanged { newIndex in self.currentPage = newIndex } // page change

Expected behavior
Pager shouldn't have negative indices or an index -1. Or at least the option to disable such feature?

Environment:

  • OSX: iOS 13.6
  • Device: iPhone X
  • SwiftUIPager version: 1.9.0.beta-3, I know it is not the most updated version, but I did not update to that new version (production release 1.9.0) because it caused new issues in the scrolling experience (hard to scroll, laggy videos).

SwiftUIPager gets stuck upon Touch cancellation

Hi Fernando,

really appreciate your work. Awesome repository. I found an issue which should be easy fixable I think.

Issue

The page freezes in its position when the touch is cancelled.

Steps to reproduce:

  1. Open the example project
  2. Open any pager
  3. Swipe a page just half way and hold it
  4. Use your other finger to drag down the control center or notification center (it can also be something else which cancels the touch for the pager)
  5. Release all your fingers
  6. Go back to the app

Result

The pager is stuck halfway.

Expected Result

The pager should snap back to a page (either the selected, or - if the travel was enough- to the next/previous one).

Gif showing the issue

ezgif com-video-to-gif (1)

Probably need to listen to a some touch cancelled event?

BR

Problem with navigationlink

Hello here,
Thanks for the libs, it looks very nice for now :)
So I'm an Android developper for several years who just started in order to publish my application on the two stores, so I may have done things wrong xD
I started SwiftUI and it looks very promising.

I have a pager inside a navigationview which contains navigationlinks such as

   Pager(page: $page,
                        data: self.zoneDetailVM.blocs,
                        id: \.self,
                        content: { item in
                            // create a page based on the data passed
                            NavigationLink(destination: BlocDetailUI(bloc: item)) {
                                pageView(item)//(bloc: item, loading: false)
                            }
                        }).itemSpacing(10)
                        .padding(25)
                        .background(Color.gray.opacity(0.2))
                        .frame(height: 200)

Everything works perfect execpt navigation.

If I remove navigation link I can scroll without any problems. But if I add it, navigation link has the priority.
I'd say it take the priority of the touch.

Any help would be appreciated .
I put the two videos to demonstrate .

Cheers

videos.zip

Precondition failure: invalid value type for attribute

I am getting crash when I try to view Image in pager.

xcode 11.5
Simulator iPhone 11 Pro
iOS 13.5

Filename: SizeViewModifier.swift
line 23
Screenshot 2020-06-11 at 12 57 30 AM

My code where I am using it.
`
import CoreData
import SwiftUI
import SwiftUIPager

struct BoardPhotoView: View {
@Environment(\.presentationMode) var presentationMode
@FetchRequest(entity: Pedalboard.entity(), sortDescriptors: [
    NSSortDescriptor(keyPath: \Pedalboard.name, ascending: true),
    NSSortDescriptor(keyPath: \Pedalboard.imageD, ascending: true)
    ]
) var pedalboards: FetchedResults<Pedalboard>

@State var image : Data = .init(count: 0)

let pedalboard: Pedalboard
let imageArray:[Data]
// Magnification vars
@State private var scale: CGFloat = 1
@State var pageIndex = 0
// drag vars
@State private var position = CGSize.zero
@GestureState private var dragOffset = CGSize.zero

var body: some View {
            
    return
        ZStack {
            Color.newPrimaryColor2
            GeometryReader { proxy in
                VStack(spacing: 10) {
                    Pager(page: self.$pageIndex,
                          data: [0],
                          id: \.self) { page in
                            self.pageView(self.imageArray[page], index:page)
                    }
                    .disableDragging()
                    .itemSpacing(10)
                    //.padding(20)
                    .onPageChanged({ page in
                        withAnimation {
                            self.scale = 1.0
                            self.pageIndex = page
                        }
                    })
                    .frame(width: proxy.size.width,
                           height: proxy.size.height)
                    .background(Color.newPrimaryColor2.opacity(0.3))
                    
                    
                    Spacer()
                    
                  /*  HStack {
                        Spacer()
                        Button(action: {
                            withAnimation {
                                self.scale = 1.0
                                self.pageIndex = max(0, self.pageIndex - 1)
                            }
                        }, label: {
                            HStack(spacing: 10) {
                                Image(systemName: "backward.fill")
                                    .padding()
                                Text("Previous")
                            }
                        }).disabled(self.pageIndex <= 0)
                        Spacer()
                        Button(action: {
                            withAnimation {
                                self.scale = 1.0
                                self.position = .zero
                                self.pageIndex = min(self.imageArray.count - 1, self.pageIndex + 1)
                            }
                        }, label: {
                            HStack(spacing: 10) {
                                Text("Next")
                                Image(systemName: "forward.fill")
                                    .padding()
                            }
                        }).disabled(self.pageIndex >= self.imageArray.count - 1)
                        Spacer()
                    }*/
                    Spacer()
                }
            }
            
    }
}

func pageView(_ imagePic: Data, index: Int) -> some View {
    GeometryReader {reader in
        //Image("pizza1delivery")
        Image(uiImage: UIImage(data: imagePic)!)
        .resizable()
        .scaledToFit()
        .cornerRadius(5)
        .scaleEffect(index == self.pageIndex ? self.scale : 1)
        .shadow(radius: 5)
           
        .animation(.default)
        .offset(index == self.pageIndex ? CGSize(width: self.position.width + self.dragOffset.width, height: self.position.height + self.dragOffset.height) : .zero)
        .gesture(index != self.pageIndex ? nil : self.scale <= 1 ? nil : DragGesture()
            .updating(self.$dragOffset, body: { (value, state, transaction) in
            state = value.translation
        })
        .onEnded({ (value) in
            self.position.height += value.translation.height
            self.position.width += value.translation.width
        })
        )
            
        .gesture(index != self.pageIndex ? nil :MagnificationGesture()
            .onChanged({ (value) in
                print(index)
                self.scale = value.magnitude
            })
            .onEnded({ (value) in
                self.position = .zero
                self.scale = value.magnitude
            })
        )
    }
}
} 
struct BoardPhotoView_Previews: PreviewProvider {
static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
static var previews: some View {
    let pedalboard = Pedalboard(context: moc)
    // pedalboard.imageD = "sampleBoard"
      return NavigationView {
        BoardPhotoView(pedalboard: pedalboard, imageArray: [pedalboard.imageD!])
    }
}}

`

EDIT: It is working fine in Xcode 11.3.1

[BUG] Vertical Pager inside ScrollView doesn't receive touches.

Hello!
At first let me thank you for this package (it's really awesome!). However is there a way to make vertical Pager work inside ScrollView? I'm trying to achieve similar effect to iOS 14 Stack Widgets Control. I've tried to change gesture priority, but sadly it didn't help. Maybe I'm doing something wrong. Thank you in advance!

Unable to swipe through pager on a button

When adding a button to a page it's not possible to swipe the pager on the button any more.

Is there a way to make this work?

Here's an example where I've added a button around the text:

    func pageView(_ page: Int) -> some View {
        ZStack {
            Rectangle()
                .fill(Color.yellow)
            Button(action: { print("Button tapped") }) {
                Text("Page: \(page)")
                    .bold()
            }
            .buttonStyle(PlainButtonStyle())
        }
        .cornerRadius(5)
        .shadow(radius: 5)
    }

pager-swipe

[REQUEST] Enable.disable scrolling programatically

Is your feature request related to a problem? Please describe.
I would like to enable/disable scrolling programatically

Describe the solution you'd like
Add a .shouldScroll(bool) modifier to Pager Where we can disable/enable scrolling

[FEAT] pull to refresh

Is your feature request related to a problem? Please describe.
Trying to make a home feed with your pager. Is it possible to add a refresh control when you try to drag down from page 1?

Describe the solution you'd like
UIRefreshControl above the pager when pulling down from page 1

Describe alternatives you've considered
Tried many hacky ways to do some "onPageChanged" refresh

Additional context
Let me know what you think. I tried searching your closed pull requests and issues but couldn't find anything related to this. (Building on iOS 13.7, project is mostly SwiftUI)

Also
Thanks for always being so responsive!! You're awesome :o

[BUG] The content of Scrollview cannot be clicked on Xcode 11

Pager contains a Scrollview and the Scrollview content is not large enough to cover the entire screen.
Starting from the second page, clicking Scrollview has no effect,
However, dragging Scrollview and clicking again will trigger the event correctly

import SwiftUI
import SwiftUIPager

struct UIttt: View {
    @State var currentPage = 0
    @State var a = 0
    var body: some View {
        VStack {
            Text("Hello, World! \(a)")
            Pager(page: $currentPage, data: Array(0..<10), id: \.self){ i in
                ScrollView {
                    Text("Hello, World!  Hello, World!  Hello, World!  Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! \n\(self.a)")
                        .onTapGesture {
                            self.a = self.a + 1
                    }
                }
            }
        }
    }
}

struct UIttt_Previews: PreviewProvider {
    static var previews: some View {
        UIttt()
    }
}

1jdjs3im89082

[BUG] ScrollView in Pager causes partially shown views

I'm trying to show multiple full-width pages that are scrollable vertically.
everything is working fine, but when you scroll vertically, you'll see some portion of neighboring views.

struct ContentView: View {
    @State var pageIndex = 0
    
    var items: [PagerData] = [
        PagerData(id: 0),
        PagerData(id: 1),
        PagerData(id: 2),
        PagerData(id: 3),
        PagerData(id: 4),
        PagerData(id: 5),
        PagerData(id: 6),
        PagerData(id: 7),
        PagerData(id: 8),
        PagerData(id: 9),
        PagerData(id: 10),
    ]
    
    func pageView(_ item: PagerData) -> some View {
        ScrollView {
            VStack {
                Color.red
                    .frame(height: 300)
                Text("item \(item.id)")
                Color.green
                    .frame(height: 300)
                Text("item \(item.id)")
                Color.blue
                    .frame(height: 300)
            }
        }
    }


    var body: some View {

        Pager(page: self.$pageIndex,
              data: self.items,
              content: { item in
              self.pageView(item)
        })

    }

}

struct PagerData: Equatable, Identifiable {
    var id: Int    
}

I don't have this issue when I use a UIScorllView inside a UIPageViewController.

here is a demo:

out1

OSX: macOS BigSur 11.0 Beta
XCode: 12.0 Beta
iOS: 14.0 Beta
iPhone 8

MagnificationGesture making Pager not scroll

Hi,
I am using your SwiftUIPager. It is looking great.
In pageview, I am adding Image. It is working fine. But when I add MagnificationGesture() on Image, scrolling get stopped. And I am not able to scroll anymore. Can you please help me to sort this problem?

[FEAT] Callback on Drag Begin to allow you to hide the keyboard like UIKit ScrollView

Is your feature request related to a problem? Please describe.
I need to resignFirstResponder when drag begins.

Describe the solution you'd like
Callback just like -onPageChanged for -onDragBegan

Describe alternatives you've considered
This one works

Additional context
I can do this and submit a PR if you'd like. I did it in the example project just like you did -onPageChanged, and it works for me.

Pager#isInitialized is never true

I am not sure if it affects anything, but

/// `true` if `Pager` already appeared in the screen at least once
@State var isInitialized = false

And:

.onAppear(perform: {
  if !self.isInitialized { // <- if false
    self.page = self.initialPage
    self.isInitialized = false // < - set to false
  }
})

Thus, isInitialized is never set to true

[BUG] The pager view behind the navigation bar

Describe the bug
The pager view behind the navigation bar

To Reproduce

Pager(page: $currentPage, data: data, id: \.self) { index in
      List(self.currentList) { info in
        StandardInfoRow(info: info)
      }
    }

Expected behavior
The navigation bar does not overlap the pager view
Screenshots / Videos
swiftuipager

Environment:

  • iOS
  • Device: iPhone 8Plus
  • SwiftUIPager version: 1.10.4

Disable swipe conditionally

I would like to disable left swipe or right swipe according to a condition.
This way, you can't move forward until a condition is satisfied.
Is that possible?
Thanks.

[BUG] Preview doesn't work as expected

Describe the bug
Canvas preview in Xcode does not render the pager.

To Reproduce

import SwiftUI
import SwiftUIPager

struct WelcomeView: View {
  @State var page: Int = 0
  var items = Array(0..<10)

  var body: some View {
    Pager(page: $page,
          data: items,
          id: \.self,
          content: { index in
            // create a page based on the data passed
            Text("Page: \(index)").background(Color.yellow).frame(width: 100, height: 100)
    }).background(Color.green)
  }
}

struct WelcomeView_Previews: PreviewProvider {
  static var previews: some View {
    WelcomeView()
  }
}

Expected behavior
Expected to have rendered view in preview.

Screenshots / Videos
Screen Shot 2020-07-21 at 16 42 36

Environment:

  • xcode 11.6 (11E708)

[BUG] explicit animations inside the pager don't seem to work

Describe the bug
Adding an animation inside the pager seems to not work without adding an .animation(.default)

To Reproduce

import SwiftUI
import SwiftUIPager

struct TestPager: View {
    @State var currentPage: Int = 0
    @State var buttonText: String = "hsduahsd"
    
    var body: some View {
        Pager(page: $currentPage, data: Array(0..<4), id: \.self, content: { _ in
            Button(action: { withAnimation(.linear(duration: 5)) {
                self.buttonText = "asdasdads"
            }}, label: {
                Text(buttonText)
            })
        })
    }
}

Expected behavior
The above code runs and animates for 5 seconds

Environment:

  • OSX: 11 Beta MacOS / iOS 14
  • Device: iPhone11
  • SwiftUIPager version: 1.11.0

Thanks for your time!

View background

In my app, I set the background of white ,but the Page allways hava a little gray color between tow pages,
how can I to cancel that

[FEAT] Scroll to Top functionality, similar to ScrollView

Is your feature request related to a problem? Please describe.
This feature is not related to a problem I currently have, but it would be nice to have a "scroll to top" animation similar to the way ScrollView scrolls to the top upon tapping the status bar/notch.

Describe the solution you'd like
Ability to scroll back or go back to the top page after tapping the notch/status bar/top if currently not at the top page. Ideally there could be an animation of "scrolling" like ScrollView's scrollToTop.

Describe alternatives you've considered
I have looked online at how other people are doing "scroll to top", but they are all ScrollViews or UITableViews. My current workaround is to have people tap a button on the bottom to return to the top page instantly (no animation, just setting current page index to 0).

How to select different page when Pager already presenting

I need to select different page when user clicks on the map pin annotation. I do not see the way how to do it. How I could swipe to different page programmatically?

    var body: some View {
        Pager(data: mapViewModel.provider.items) { item in
            VStack {
                Text("OHO")
            }
        }
        .onPageChanged({ self.mapViewModel.selectPinAt(index: $0) })
        .firstPage(mapViewModel.selectedItemIndexSubject.value ?? 0)
        .itemSpacing(10)
        .itemAspectRatio(0.85, alignment: .end)
    }

Unable to set the desired page dimensions

Hey @fermoya, another issue from me.

I'm just playing with the component a bit to see if it fits my needs. Feel free to close these issues if these uses cases are not something you would like to support.

I'm trying to create a pager that looks something like this:

image

A pager with pages with an itemAspectRatio greater than 1 so they're wider than they're tall with only a little bit of padding on the top and bottom. Also, I would like to see quite a bit of the next page.

If I try to create this by using itemAspectRatio my pager always has a lot of padding on the top or bottom. There seems to be no way to fix this with the current attributes. I'm fairly new to SwiftUI so please correct me if there is a way to accomplish this.

I've taken your sample and added 2 sliders for the itemAspectRatio and frame.height to play with but I can't make it work.

image

The closest I get is by setting the itemAspectRatio to 0 but that gives me a pager where I can barely see the next card.

image

Wouldn't it be an option to make the pageSize an attribute the user can set directly?

.pageSize(width: CGFloat?, height: CGFloat?)

That would give a lot of freedom and open the component up to a lot more use cases!

Or even on a per page basis, that would make the component even more flexible:

Pager(
    page: self.$page1,
    data: self.data1,
    id: \.self,
    pageSize: { page -> CGSize in
         // calculate size for page
    }
) {
    self.pageView($0)
}

Looking forward to hearing your thoughts!

ScrollView inside Pager() caused vertical padding issue

When inserting a View() that has a fullscreen ScrollView() in Pager(), the Pager() will have a padding issue on the top and the bottom of the ScrollView(). From my findings (not sure), the Pager() does follows the safe area while the View() inside the Pager() doesn't causing the Pager() to cut some portion of the ScrollView() out. This can be seen in the following photo:
image

ContentView()

`
import SwiftUI
import SwiftUIPager

struct ContentView: View {
    @State private var page: Int = 0
    let items = [1,2,3,4,5]

    var body: some View {
        TabView() {
            Pager(page: self.$page,
                  data: items,
                  id: \.self,
                  content: { item in
                    SecondView()
            })
                .tabItem {
                    Image(systemName: "star")
            }
            Text("Hello")
                .tabItem {
                    Image(systemName: "circle")
            }
        }
    }
}

`

SecondView()

`
import SwiftUI

struct SecondView: View {
    @State private var pickerState: Int = 0

    var body: some View {
        ScrollView() {
            VStack() {
                Circle()
                    .frame(width: 200, height: 200)
                Text("This is a circle")
                Picker(selection: $pickerState, label: Text("")) {
                    Text("Choice 1").tag(0)
                    Text("Choice 2").tag(1)
                }.pickerStyle(SegmentedPickerStyle())
            }
            .padding(.horizontal)
            .padding(.top)
    
        }
    }
}

`

I found the temporary fix which is to add padding to the ScrollView() but this will cause a small white line between the view and the nav bar as follows:

image

image

image

image

[BUG] Easier gesture recognition

Is your feature request related to a problem? Please describe.
I need to make a very exaggerated drag to scroll to the next item. I'd like to "flick" to the next page. TikTok would be the ideal scroll-ability/flick-ability example. Single page scrolling.

Describe the solution you'd like

  • Quicker drag recognition
  • Drag distance > 60pts with velocity equals scroll to next
  • Low velocity paired with the short drag distance to scroll
  • Basically assume interaction means the user intends to scroll. Default to scrolling to next.

Describe alternatives you've considered
None yet. This is by far the best SwiftUI paging scroller I've found. Nice work.

Additional context

How to update based on parent? Eg. Change in orientation.

When the orientation of the device changes or the window is resized in macOS. The page item does not take up all the available space. Is there a way to do that? Thanks!

import SwiftUI
import SwiftUIPager

struct MainView: View {
    
    @State var page: Int = 0
    @State var pages = Array(0..<2)
    
    var body: some View {
        Pager(page: self.$page, data: self.pages, id: \.self, content: { page in
            PageView()
        })
            .itemSpacing(10)
            .expandPageToEdges()
    }
}

struct PageView: View {
    var body: some View {
        Rectangle()
            .foregroundColor(.red)
    }
}

Video

No way to set pager height depending on children height or, that children would be on the bottom

I need that pager children would be located at the bottom of the screen. It is possible to do it in two ways:

  1. Change pager height deepening on child height.
  2. Make, that children will alway be attached to the bottom of the screen.

How to make that children will be attached to the bottom?

I did not manage to do it using Spacer, ZStack, VStack and other ways. In the screenshot presented what I am getting using this code:

    var body: some View {
        VStack {
            Spacer()
                .frame(height: 400)
            Pager(page: $pageIndex, data: items) { _ in
                HStack {
                    Spacer()
                MyCellView(model: self.items[self.pageIndex])
                }
            }
            .itemSpacing(10)
            .padding(8)
            .itemAspectRatio(0.9)
            .background(Color.red)
        }
    }

Screenshot 2020-02-24 at 15 07 21

Every page is initialized at the same time

I want to know if there is some way to load on demand each page. Now is calling onAppear method in each page at once.

Also the init method on each view is called multiple times on change active page. Maybe it could be a performance problem with heavy operations. Is there any way to solve it?

Tthanks for the help.

When removing last element of input data, Pager is blank

@State var notifications = [
  "You have run out of steam in your engine, buddy!",
  "Your dragon is feeling healthy",
  "This is a long, long tale of magic and heroes. So sit down and listen carefully."
]

var body: some View {
  VStack(spacing: 16) {
    if notifications.count > 0 {
      Pager(data: Array(0..<notifications.count), id: \.self) { index in
        self.bannerView(self.notifications[index])
          .onTapGesture {
            self.notifications.remove(at: index)
          }
      }
        .itemSpacing(8)
        .itemAspectRatio(0.9)

    }
  }
}

If I click on second message, everything works fine. If I click on 1st message, everything works fine. If I click one by one from first message to last, everything works as expected. But if I scroll to the last element and click on it, the whole Pager view becomes blank.

Working as expected:
Image from Gyazo

Not working as expected:
Image from Gyazo

[FEAT] Boolean to disable page scrolling

Is your feature request related to a problem? Please describe.
I'm using navigationview while working with the pager and when I'm using a navigation I can scroll past it and I don't want it to.

Describe the solution you'd like
A boolean binding variable that I can switch back and forth.

Additional context
I modified a version of this and it can be done but I would like it on the official package that way I don't have to use an outdated package. I can create a fork of my proposed solution.

Implementing NavigationLink

Hi there @fermoya ,

Using SwiftUIPager for my project. How can I use NavigationLink with SwiftUIPager, tried the following but couldn't get it to work:

NavigationLink(destination: SomeView()) {
    Pager(...)
}

Any ideas?

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.