Nesting UIScrollViews and getting info, when the scrolling happened - ios

I tried reading the other questions about similar cases, but no luck, so here’s a question.
I have a setup of nested UIScrollView’s and while the scrolling all happens fine, I don’t seem to any of the delegate events.
Outer UIScrollView is paginated and scrolls vertically. Inside I have multiple "rows" of horizontally scrolling paginated UIScrollViews. Now, what I want, is to force the inner scrollers to a specific location when vertical scroll happened.
I made the ViewController a delegate and am calling the scrollViewDidScroll, but it only fires when I initially set up the scrollers and never if I actually touch the device and scroll the grid.
I’m listening both inner and outer scrollers and they both ignore firing any events when I scroll on the device.
I’m quite new to Obj-C and Cocoa dev, so I might be completely missing something obvious, but any help is appreciated.

I created a small test project and it all seemed to work fine. Make sure to call [scrollView setDelegate: self] on all of the scrollViews (assuming the controller itself is the delegate. Apart from that, always make sure the scrollViews have a bigger contentSize than their frames, if not then scrollDidScroll: shouldn't fire at all.
My small project basically had one pointer to the outer scrollView and an NSArray of all the inner scrollViews, every time the outer one scrolled (just had a small check in the scrollViewDidScroll: delegate method) I simply recursively changed all the inner scrollViews' contentOffsets to CGPointMake(0.0, 0.0).
This isn't the most optimised approach as actually only the previously visible scrollViews would need to be reset (all the other ones should already be at that offset anyway). Also, as a side note, depending on the number of inner scrollViews this approach of adding them all to the outer scrollView may severely damage performance (the app needs to have all the subviews in memory), so you may want to think about reusing scrollViews that are not currently visible (Apple sample project PhotoScroller has some neat tricks on reusing subviews in a UIScrollView, which basically mimics how UITableView works).
If you have any other issues, let me know I'll post the sample project.

Related

How to have a reusable scroll like UITableView or UICollectionView in SwiftUI that support all scroll APIs and also indexPathForItem(at:)?

My Goal:
I want to have a reusable scroll like UiTableview in SwiftUI to load views that are not in the same Height sizes also want to control the scroll. Something like Instagram Explore view. Basically, the data source will grow as user scroll more. So I can benefit from the following:
Item Reusability
Scroll To a point
Get the index or id of an item at CGPoint
ScrollToItem
Creating snapping to a point based on Drags and Deceleration
Memory-optimized
My minimum target for this project is iOS 15.
ScrollView:
I tried ScrollView with ScrollViewReader, but even if I use the LazyVStack it will not perform well in case of memory,LazyVStack will create a view when it is needed but after that, it will stay in memory. Also, I can not scroll to a CGPoint, just to the predefined Id.
I can not use .content.offset because then the ScrollViewReader stops working. Also, by using .content, the ScrolView loads all the data at once in memory. I can not get the benefits of controlling its offset.
ScrollView with lazyVGrid doesn't perform well when I want to control the scrollView; it causes so many jumps and unwanted lags and also the same issue that I can not control so much on Scroll and drags. apple suggests this combination for loading a long list, but again you can not control it because it is based on ScrollView.
Adding Drag gesture as .simultaneousGesture to scrollView to get drags is only possible if I set the minimumDistance = 0.0, which means ScrollView will not scroll anymore. so I can not have the combination of scroll and accessing the velocity of user drags.
List:
The list is good in memory and reusability, although you will have the same issues regarding the scroll. But it has another big disadvantage. ScrollViewReader is not well supported for List. it causes crashes in the app., there are a lot of threads in apple forums and StackOverflow that mention this problem.
StackOverFlow thread. apple thread.
I have been working to solve this issue for 2 weeks, but all of them lead me to the point that SwiftUi is not powerful enough for large projects to create an enterprise app that works smoothly and reliably.
So is there any good solution to using SwiftUI for these problems?

How to pin an image/view inside a UIScrollView when scrolling

I've been searching for a way to pin views/images to the top of a UIScrollView when scrolling. However the posts/articles I came across are not in swift 3. I'm not sure if I'm typing my question in the web correctly. So my question is how can we achieve the same behavior as a UITableView or UICollectionView. When you scroll, a section will stick to the top until another section pushes it up. I'm wondering would we be able to use views/image and pin them at the top of the UIScrollView. Down below is a screenshot of a UIScrollView that has 4 views.
So when scrolling I would like to pin the first view/image to the top until another view/image pushes it. Also would it be possible to determine which view sticks to the top. So lets say I only want the red views to stick until another red view pushes it. Been looking for a way to achieve this type of behavior for a while now.
Please help, would really appreciate any help provided at this point. Thanks.
A few ways to do this, but you can use the scrollView delegate’s scrollViewDidScroll to capture the contentOffset and use a combination of the target view’s origin/center/transform properties to keep the view where you want it.
There’s a neat video explaining how to do this that Apple released during WWDC 2010, called something like “Advanced Scroll View Behavior”, if I remember correctly. It’s definitely worth a watch.

Very slow deceleration in UIScrollView just in certain cases

I am struggling with aligning a screen to a certain Views inside a UIScrollView. I would like to have same behaviour as paging (same fast and smooth deceleration) but with alignment to a custom views instead of stoping on multiples of the scroll view’s bounds. I have implemented delegate method scrollViewWillEndDragging(_:withVelocity:targetContentOffset:) in order to define my own scroll view’s bounds location. I have also set decelerationRate to UIScrollViewDecelerationRateFast.
It work mostly as desired except the cases when alignment animation is very slow. To be more precise, sometimes, after the dragging is finished, the scrolling animation decelerates to final point very slowly. It finally reach the correct point but after a longer while. I am not able to track down the cases when it happens. I am able to say that everything works fine when final velocity of dragging is zero. Thus, it happens only in some cases when dragging is finished with nonzero velocity.
I wonder if somebody had same problem since I wasn't able to google anything useful. Can you help me please?
I have solved this problem by implementing delegate methods scrollViewDidEndDragging and scrollViewWillBeginDecelerating. Then by using information from scroll view’s pan gesture I found out about translation and I determined final scroll view’s bounds location using setContentOffset method on scroll view.

scrollsToTop and multiple scrollViews on-screen

I'm currently working on an iPad app that should feature two UITableViews side-by-side (unfortunately, they can't be combined into a single one for some specific reasons).
As far as I know, scrollsToTop won't work properly for both of them, since only one scrollView should have this property enabled at a time. The thing, the app could really use this feature for both tableViews, but I haven't figured out how to do it.
Is there any workaround that would allow me to have scrollsToTop enabled and working for both of them? Or maybe disabling it on both and then listening for touches on the status bar so the app can determine above which tableView they happen and scroll as appropriately? Or any other clever solutions?
Edit: tableViews shouldn't scroll up together. Each should scroll only if the tap on the status bar is right above them (just like if we had two iPhones side by side, for the lack of a better example).
If you have many scrollviews this category is really useful.
-[UIScrollView makeOnlyThisScrollViewScrollToTopOnStatusBarTap];
https://gist.github.com/hfossli/6776203
It basically sets scrollsToTop to NO on all other scrollViews than the one you are specifying + taking care of the default value.
There's a scroll view delegate method scrollViewShouldScrollToTop:. Implement the method on one table view and in the method body tell the other one to also scroll to the top (probably with setContentOffset:animated:).

Enlarging swipe angle in which a horizontal swipe is recognized in nested UITableView

I am trying to build a UI in iOS in which there is an outer paging UIScrollView inside of which are multiple UITableViews, each representing a page. The problem I am encountering is that the angle at which I must swipe to trigger the outer paging UIScrollView to scroll horizontally is too narrow. Here is a screenshot showing what I mean.
Essentially a user has to swipe at a near-perfect horizontal angle in order for the UITableView to ignore the gesture and pass it along to the outer UIScrollView, thereby triggering a horizontal page movement. This behavior quickly becomes annoying.
I have done extensive research on stack, to no avail. Some people suggest setting directionalLockEnabled, but that has no effect here (the inner UITableView has its direction locked, it's just too greedy with the angles it interprets as going in its direction). Others suggest modifications to the contentSize, but the contentSize of the views is, in point of fact, correct. Still others suggest using Gesture Recognizers, but those seem to have a delay before they kick in.
Can anyone suggest how to accomplish the behavior in the image above?

Resources