amirdew / collectionviewpaginglayout Goto Github PK
View Code? Open in Web Editor NEWA simple but highly customizable UICollectionViewLayout for UICollectionView -- Simple SwiftUI views that let you make page-view effects.
License: MIT License
A simple but highly customizable UICollectionViewLayout for UICollectionView -- Simple SwiftUI views that let you make page-view effects.
License: MIT License
when we reload collection view using "collectionView.reloadData()", the cells disappears. After moving in the blank space the cell appears back
In the latest version of Xcode 12, there is a warning associated with this package if installed via SwiftPM. This is because Xcode 12 drops support for iOS 8 and this package still supports it.
Screenshot
I add this code below(PagingLayoutSamples Project), It works well in simulator but in real device all transformed cells disappear.
(collectionView.isPagingEnabled = false) && content offset is not in paging location
extension ShapesViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard collectionView != self.layoutTypeCollectionView else { return }
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0)
defer { UIGraphicsEndImageContext() }
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
let imageView = (self.view.viewWithTag(432) as? UIImageView) ?? UIImageView(frame: CGRect(x: 30, y: 50, width: 190, height: 340))
imageView.contentMode = .scaleAspectFit
imageView.tag = 432
self.view.addSubview(imageView)
imageView.image = image
}
}
How can I solve this?
Hi,
I am facing the issue that right after launching the app, initially the wrong images/text are displayed. After scrolling back and forth, the correct images/text are displayed. Any idea?
Add the Codes below BaseShapeCollectionViewCell.swift
.
// MARK: Private functions
private func setupViews() {
shapeCardView = ShapeCardView.instantiate()
let leftRightMargin = frame.width * 0.18
let topBottomMargin = frame.height * 0.06
edgeConstraints = contentView.fill(
with: shapeCardView,
edges: UIEdgeInsets(top: topBottomMargin, left: leftRightMargin, bottom: -topBottomMargin, right: -leftRightMargin)
)
clipsToBounds = false
contentView.clipsToBounds = false
/*===============================================================
Add this codes, And It doesn't work on the cell > 0,
It only work with Cell which IndexPath.row == 0
===============================================================*/
let btn = UIButton.init(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
btn.addTarget(self, action: #selector(onTap), for: UIControl.Event.touchUpInside)
btn.setTitle("Tap Me", for: UIControl.State.normal)
btn.setTitleColor(.red, for: UIControl.State.normal)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 30)
shapeCardView.addSubview(btn)
}
@objc func onTap() {
print("onTap: ", self)
}
Hello there,
I'm refreshing my collectionView whenever a data is supplied by the viewModel.
viewModel.listHomePage.asObservable().subscribe(onNext: { [weak self] responseModel in
if responseModel != nil , let safeSelf = self {
DispatchQueue.main.async {
safeSelf.collectionView.reloadData()
}
}
}).disposed(by: disposeBag)
This observable is fired twice by our app cache mechanism.
First fire is occuring immediately by the latest cached remote server data (if exists),
Second fire is occuring by the network request with the latest remote server data when the request is finished.
When first fire is occured, I see everything is fine, my collection view is there, however when the network request finish and the second fire is occured, the collectionview is suddenly disappearing from the screen, it comes to visible until I physically touch anywhere in the collection view.
When I search if there is a similar issue, I found your reply:
then, I have changed my code to:
viewModel.listHomePage.asObservable().subscribe(onNext: { [weak self] responseModel in
if responseModel != nil , let safeSelf = self {
DispatchQueue.main.async {
safeSelf.collectionView.reloadData()
safeSelf.collectionView.performBatchUpdates({
safeSelf.influencersCollectionView.collectionViewLayout.invalidateLayout()
})
}
}
}).disposed(by: disposeBag)
Now, when the second fire is occured, I see collectionView refresh itself and in a moment it is going completely and recoming to screen in a very short time. (because we invalidate all layout). It is okey now, but it is still annoying to see collectionview is blinking.
Is it normal to see this problems when the second reloadData called? You have wrote that we don't need to call invalidateLayout anymore, but I'm facing this error if I don't use it, that's why I'm creating this issue.
Thank you.
Is it possible to arrange the UICollectionViewCells in a vertical orientation?
Changing the scrollDirection
only changes how I interact with the UICollectionView itself.
The items keep being shown from left to right.
The collectionView is working fine with your pod, but I am not able to scroll vertically in the storyboard when trying to scroll with my finger over the UICollectionView, I need to put my finger on other items and then scroll to let it work.
Any solution?
I've tried calling CATransaction.setAnimationDuration: In CollectionViewPagingLayout.safelySetCurrentPage(_ page: Int, animated: Bool)
,but it dosen't work, and when using UIView Animation to animate the colletionView.setContenetOffset
, it behaves weirdly
when we use internalization for different languages like arabic language written from right to left, application layout changes direction with leading to trailing. but swipe direction of CollectionViewPagingLayout does not change.
Crashes when swiping up on the controller when having a collectionView with this layout presented modally. Only happens when the view is initially loaded and the first thing you do is swipe up.
Will not crash when you swipe to a different card and swipe up. When you swipe up and it doesn't crash you get other cards reloading from the top left and expanding back into place every time you swipe.
If you comment out viewDidLayoutSubviews code it will not crash but other issues are present.
Error:
[UICollectionViewData isIndexPathValid:validateItemCounts:]: message sent to deallocated instance
https://drive.google.com/drive/folders/1Bd1390w9pvN6dBGFl1qYxt8a7mXB23fX?usp=sharing
I have attached a video link of the issue on the card stack.
When we try to swipe up the card for the first time it bounds up the whole stack rather than just swiping up the only card we have selected.
I try to add shadow to around the cell which use CollectionViewPagingLayout, but the shadow show in the wrong place. how can I achieve it
in swiftui code for latest release 1.0.3 through swiftpm
\Swift/ContiguousArrayBuffer.swift:580: Fatal error: Index out of range
2022-03-14 12:07:46.711332-0300 app[5708:2182143] Swift/ContiguousArrayBuffer.swift:580: Fatal error: Index out of range
error: Execution was interrupted, reason: EXC_BREAKPOINT (code=1, subcode=0x186695a64).
The process has been returned to the state before expression evaluation.
This issue shows up in shutdown/init sequence where a viewcontroller is dismissed and afterwards the model data and the selection (stored in a class passed as a parameter to the viewcontroller) are reset for use the next time the viewcontroller is invoked. Not sure how currentPage ends up being -1, but it would be prudent to have a check for >= 0 also. Not sure if its down to a retain cycle that the data model is still connected to ScalePageView - I was expecting that it would be deinitialised fully on dismissing the viewcontroller. It is a viewcontroller with only a UIHostingController with a SwiftUI view with ScalePageView inside it.
private func setupOnCurrentPageChanged(_ viewController: ViewController) {
viewController.onCurrentPageChanged = { [data, selection] in
guard $0 < data.count else { return }
selection?.wrappedValue = data[$0].id
}
}
public func onCurrentPageChanged(layout: CollectionViewPagingLayout, currentPage: Int) {
onCurrentPageChanged?(currentPage)
}
(lldb) print $0
(Int) $R0 = -1
(lldb) print data
([Info]) $R1 = 0 values {}
(lldb) print selection
(SwiftUI.Binding<String?>?) $R2 = some {
transaction = {
plist = {
elements = nil
}
}
location = 0x0000000281a1ad00 {
SwiftUI.AnyLocationBase = {}
}
_value = nil
}
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000001853f21a8
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [330]
Triggered by Thread: 0
Kernel Triage:
VM - pmap_enter failed with resource shortage
VM - pmap_enter failed with resource shortage
Thread 0 name:
Thread 0 Crashed:
0 libswiftCore.dylib 0x00000001853f21a8 closure #1 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 300 (AssertCommon.swift:96)
1 libswiftCore.dylib 0x00000001853f1f48 closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 220 (AssertCommon.swift:89)
2 libswiftCore.dylib 0x00000001853f183c _assertionFailure(_:_:file:line:flags:) + 232 (AssertCommon.swift:85)
3 libswiftCore.dylib 0x00000001853ce594 _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds(_:wasNativeTypeChecked:) + 280 (ContiguousArrayBuffer.swift:580)
4 libswiftCore.dylib 0x00000001853d3380 Array.subscript.getter + 88 (Array.swift:383)
5 app 0x0000000100afacf8 closure #1 in PagingCollectionViewControllerBuilder.setupOnCurrentPageChanged(_:) + 372 (PagingCollectionViewControllerBuilder.swift:77)
6 app 0x0000000100af9b40 specialized PagingCollectionViewController.onCurrentPageChanged(layout:currentPage:) + 60 (PagingCollectionViewController.swift:53)
7 app 0x0000000100ae2590 currentPage.didset + 84 (CollectionViewPagingLayout.swift:62)
8 app 0x0000000100ae2590 currentPage.set + 88 (CollectionViewPagingLayout.swift:0)
9 app 0x0000000100ae2590 CollectionViewPagingLayout.updateCurrentPageIfNeeded() + 468 (CollectionViewPagingLayout.swift:245)
10 app 0x0000000100ae2308 CollectionViewPagingLayout.invalidateLayout() + 72 (CollectionViewPagingLayout.swift:225)
11 app 0x0000000100ae23ac @objc CollectionViewPagingLayout.invalidateLayout() + 28 (<compiler-generated>:0)
12 app 0x0000000100ae4db4 partial apply for closure #1 in closure #1 in CollectionViewPagingLayout.invalidateLayoutInBatchUpdate(invalidateOffset:) + 68 (CollectionViewPagingLayout.swift:134)
13 app 0x0000000100ae4df0 partial apply for thunk for @callee_guaranteed () -> () + 20 (<compiler-generated>:0)
14 app 0x0000000100ae20f8 thunk for @escaping @callee_guaranteed () -> () + 20 (<compiler-generated>:0)
15 UIKitCore 0x0000000183183328 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:animator:animationHandler:] + 420 (UICollectionView.m:10402)
16 app 0x0000000100ae20a8 closure #1 in CollectionViewPagingLayout.invalidateLayoutInBatchUpdate(invalidateOffset:) + 572 (CollectionViewPagingLayout.swift:133)
17 app 0x0000000100ae0838 thunk for @escaping @callee_guaranteed () -> () + 20 (<compiler-generated>:0)
18 libdispatch.dylib 0x0000000180671924 _dispatch_call_block_and_release + 32 (init.c:1517)
19 libdispatch.dylib 0x0000000180673670 _dispatch_client_callout + 20 (object.m:560)
20 libdispatch.dylib 0x0000000180681b70 _dispatch_main_queue_callback_4CF + 944 (inline_internal.h:2601)
21 CoreFoundation 0x00000001809b9d84 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16 (CFRunLoop.c:1795)
22 CoreFoundation 0x0000000180973f5c __CFRunLoopRun + 2540 (CFRunLoop.c:3144)
23 CoreFoundation 0x0000000180987468 CFRunLoopRunSpecific + 600 (CFRunLoop.c:3268)
24 GraphicsServices 0x000000019c52b38c GSEventRunModal + 164 (GSEvent.c:2200)
25 UIKitCore 0x000000018332a5d0 -[UIApplication _run] + 1100 (UIApplication.m:3493)
26 UIKitCore 0x00000001830a8f74 UIApplicationMain + 364 (UIApplication.m:5047)
27 app 0x000000010094c4c0 main + 68 (CircleButton.swift:22)
28 dyld 0x0000000100eb5aa4 start + 520 (dyldMain.cpp:879)
Hi,
Is it possible to implement a tinder-like interface with the lib? For example, instead of paging through cards, I want the user to go through cards and swipe them into right and left collections.
Hi, thanks for the project, its super impressive. I saw some other issues people are having with the project and tried to use the suggestions you provided, but I am still facing some issues myself.
It seems like, by design, the cells always take the entire collection view space. Is there any way that I can have multiple cells appear in the collection view?
The issue i'm having is that the appearance is like this:
The animations of the cells (scaling as I Scroll) work fine, but the other cells appear off screen.
I think that if my collection view had a larger height, then it would appear normal, but I need to have a small collection view size. I tried manipulating the card size like in other examples you had, but it seems like just the card would get smaller and the cell would remain the same size as the collection view.
support infinite loop model
Hello again,
I'm using .perspective stack transform with stackPosition: .init(x: -1.00, y: 0.00)
So instead of right listing of back cells, I put them on the left side.
However now, mouse gestures is not working as expected,
Please see attachment video:
I want to pop the top cell by swiping right, not swiping left. How to do that?
My whole configuration:
var stackOptions = StackTransformViewOptions(
scaleFactor: 0.10,
minScale: 0.20,
maxScale: 1.00,
maxStackSize: 6,
spacingFactor: 0.08,
maxSpacing: nil,
alphaFactor: 0.15,
bottomStackAlphaSpeedFactor: 0.90,
topStackAlphaSpeedFactor: 0.30,
perspectiveRatio: 0.30,
shadowEnabled: true,
shadowColor: .black,
shadowOpacity: 0.10,
shadowOffset: .zero,
shadowRadius: 5.00,
stackRotateAngel: 0.00,
popAngle: 0.31,
popOffsetRatio: .init(width: 2.00, height: 0.30),
stackPosition: .init(x: -1.00, y: 0.00),
reverse: false,
blurEffectEnabled: false,
maxBlurEffectRadius: 0.00,
blurEffectStyle: .light
)
Thank you.
Hi,
Thanks for the great work on this. A question: can you suggest how I can make a continuous looping of a ScalePageView linear scroll view? I'd like to have it on a timer so it shows a cell for like 2 seconds then goes to the next and at the end shows the first one again.
Thanks,
Dave
I am using CollectionViewPagingLayout for a SwiftUI project with deployment target iOS 14.0, but when archiving the project using version 1.0.3 via SPM, I am getting around 20 errors about not finding SwiftUI. As of what I understood, the xcode13-no-swiftui branch is meant for projects that support older iOS versions and don't need SwiftUI or Combine, but I do need SwiftUI and Combine compatibility.
Is there a workaround to fix this?
When you set
collectionView.semanticContentAttribute = .forceRightToLeft
I believe that the expected behavior should be that the collectionView scrolls from right to left, instead of the standard left to right which is the default in most western languages. However, this has no effect.
Hi,
everything is working very well with the collection view interaction. However, after several swipes I keep getting the following error:
Thread 1: "It is an error to release a paused or stopped property animator. Property animators must either finish animating or be explicitly stopped and finished before they can be released."
Any idea what I can do about it?
Thanks.
I'm using Xcode 13 (Beta 5) and iOS 15 Beta and have just started using this package. However, as soon as I include the package in my project I'm getting "Build Failed" and a couple of warnings.
In total I get 10 swift compiler errors related to CollectionViewPagingLayout and two warnings, see attached print screen.
Any ideas?
Thanks!
I run example on iPhone 6s, ios 12.4 and choose option Fruits, It cash with reason:
UICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x2826b1220> {length = 2, path = 0 - 5}
I want to get the topmost card's index which is being displayed. Pl guide me on how I can get it with your library. Thanks.
When calling setCurrentPage
with a specific page number, the internal variable currentPage
in CollectionViewPagingLayout
is not updated.
This causes a possibly far to large step, even in the wrong direction when goToNextPage
or goToPreviousPage
is called.
First of all, thanks for creating this great library @amirdew!
I found some issue when using a dragGesture and an offset on the CollectionView cell. Below I laid out my observation and added a full example.
Let me know if you have any related questions or need more information from my side!
ScalePageView(...) { _ in
CellView()
.offset(...)
}
.options(.layout(.linear))
.gesture(...)
ScalePageView(...) { _ in
CellView()
.gesture(...)
.offset(...)
}
.options(.layout(.linear))
import SwiftUI
import CollectionViewPagingLayout
struct Item: Identifiable {
let number: Int
var id: Int {
number
}
}
let items = [
Item(number: 0),
Item(number: 1),
Item(number: 2)
]
struct ContentView: View {
@State var verticalOffset: CGFloat = 0
var body: some View {
ScalePageView(items) { _ in
Rectangle()
.fill(Color.orange)
.offset(y: verticalOffset)
.gesture(dragGesture)
}
.options(.layout(.linear))
.pagePadding(.absolute(50))
}
var dragGesture: some Gesture {
DragGesture()
.onChanged {
verticalOffset += $0.translation.height
}
.onEnded { _ in
verticalOffset = 0
}
}
}
Hi,
When the user is swiping pages, is there a way to create a delay before next page is displayed? Should I use the transform func for that?
I have an UICollectionView
with 4 visible items stacked with the StackTransformView
conformance. I configured the stack appearance with this code
StackTransformViewOptions(scaleFactor: 0.3, minScale: 0.2,
spacingFactor: 0.07,
popOffsetRatio: .init(width: 0, height: 3), reverse: true)
The problem is that the last visible cell has an undesired spacingFactor. I want to change the spacing factor for only that last visible cell, is it possible?
NewWD.dataSource = self //coforms with UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
NewWD.delegate = self
let layout = CollectionViewPagingLayout()
layout.delegate = self // conforms with CollectionViewPagingLayoutDelegate
layout.numberOfVisibleItems = 3 // default=nil means it's equal to number of items in CollectionView
NewWD.collectionViewLayout = layout
NewWD.isPagingEnabled = true
//this function is never called
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let needH = self.NewWD.frame.height - 20
let needW = needH * 0.56
return CGSize(width: needW, height: needH)
}
Hello, I encountered a problem with UI debugging or description during the development process, but I didn't find the corresponding description. I don't know if there is a solution description for the iOS APP?
Build input file cannot be found: '/CollectionViewPagingLayout/Info.plist'
I've build an app which will allow user to like and share quotes by using your library but the problem is that after a swipe next cell action buttons are not capable. We've tried to fix this issue by using your delegate method but it is not working.
Video attached - https://youtu.be/yVZlXM_TXbE
I have a ScaleTransformView, with a layout of 3 visible cells, that seems to work as expected.
However, when calling setCurrentPage(animated = false), each of the 'visible cells' occupy the same frame as the 'primary' cell, causing a stack of three cells. As soon as you start dragging the primary cell, each of the 'visible cells' fall to their correct locations respectively.
Setting animated: true results in expected behavior, however having it as true isn't ideal for my use case. I've already tried calling layoutIfNeeded(), but that seems to have no effect.
Alright this is a bit weird let me see if I can explain it well enough. I think it is fairly easy to replicate also.
Basically when using the StackTransformView
if you have a UITextView that is suppoused to scroll then it won't detect the touch on the second cell and onwards and you won't be able to scroll.
Looking at it more closer I decided to check the example.
I decided to add the event to detect when a cell is selected in ShapesViewController
like this:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.collectionView {
print("Clicked cell at")
print(indexPath)
}
}
Now when trying the ScaleTransformView
examples or SnapshotTransformView
when you tap on the second cell the output will in console will be Clicked cell at [0, 1]
if you tap the third then it should be Clicked cell at [0, 2]
.
But when doing the same with StackTransformView
the output is always the same Clicked cell at [0, 0]
.
It seems as if the animation to change cell is done and the contentview of the cell is moved and animated but the first cell is always in the front capturing the touches.
Another way to see this is by replacing the ImageView
in the ShapeCardView.xib
stackView for a TextView
. Because the first cell seems to be always in the front capturing the touches you won't be able to scroll text on the second or third cell and so on.
It is kind of weird and I tried to look into it but have no idea what is going on.
Surprisingly if we apply our own transform into TransformableView
like it is done on the credit card example in CardCollectionViewCell
this problem is not there and touches go to their respective cells and scrolling is possible in the text view.
let attributes = myCollectionView.layoutAttributesForItem(at: IndexPath(row: 0, section: 0))!
let cellFrameInSuperview = myCollectionView.convert(attributes.frame, to: myCollectionView.superview)
Doing the above simply returns the entire collectionView's frame. Is there a way to limit the frame to just the presented center cell?
I have 10 UICollectionViewCell (each with an image inserted) in UICollectionView, and initially due to restrictions on the rate of downloading images I had reduced the number of visible items to 5
layout.numberOfVisibleItems = 5
When I go to change my data model and do a full reload of the UICollectionView, depending on what UICollectionViewCell Im on....ie the value for layout.currentPage
and layout.currentScrollOffset
determines which 5 cells will be loaded in memory after the reloadData()....
.e.g. currentPage is 9, do a reloadData()
, and cells 5,6,7,8,9 are loaded into memory. then I setCurrentPage()
to 2
for the UICollectionViewCells 0 and 1 behind page 2, the images displayed are from the recycled UICollectionViewCell taken from memory like if they were cached - even though the new images have been downloaded again and set in the imageView in the UICollectionViewCell
Initially I was thinking:
reloadData()
call and setCurrentPage()
but these are both performed on the main thread.invalidateLayout()
before and after the reloadData()
layout.currentPage
before doing the reload.DispatchQueue.main.async {
self.carouselView.collectionViewLayout.invalidateLayout()
self.carouselView.layoutIfNeeded()
self.dataModel = newDataModel
self.carouselView.reloadData()
layout.setCurrentPage(index, animated: true)
}
But when I don't set layout.numberOfVisibleItems
and leave it at the default, the issue with showing the wrong images behind the currentPage does not happen. There is also no need for invalidateLayout()
here
So I am wondering if I'm missing something when using layout.numberOfVisibleItems
or if there is a bug there
Apart from TransformableView if i try any other of the three options it gives me same error cannot find in scope.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.