Problems animating UICollectionView bounds with a nonzero contentOffset - ios

Sigh. Second question in two days. I'm starting to not like UICollectionView quite so much.
Ok. I have a UICollectionView set up, working nicely, and a custom layout object managing the... er... layout. I'd post code but there's tons of it and I'm not certain which parts would be relevant. Mostly I just followed this tutorial: http://skeuo.com/uicollectionview-custom-layout-tutorial
Now herein lies the problem. If I scroll my collection view away from contentOffset 0,0, then cause its bounds to animate, the start location of any cells that were already visible and are remaining visible is a long way out! They arrive at the right place, but start about 60 pixels too far left and too far down. Always the same amount each time no matter how far I've scrolled from 0,0. The weird thing is, so long as I don't move away from 0,0 it all animates exactly as I want it to, no problems whatsoever. Somehow the contentOffset appears to be interfering with the current frame of my content cells, and in a completely nonsensical way.
It's really bizarre and I can't work out why it's happening. Anyone got a clue?
[EDIT] a little more information. As an experiment, I changed the layout style of my collection view so that it only changed width rather than height when it resized. Now it appears that the starting Y positions are correct, so it could well be an unusual bug involving autoresizing. For the record, I have checked my various cells and they are all set to top/left, fixed width and height, so that's not the problem.
[EDIT] And more information. It seems that, when the bounds changes, the layout view is asked if it should invalidate. Returning YES is the only way to animate the bounds change. However, what THEN happens is that the collectionView immediately sets its own bounds a second time to send the current offset back to zero/zero. The problem may be that if I do both, it gets confused, but if I only invalidate on the first call it gets the wrong information, whereas if I don't invalidate on the first, I don't get the second at all. Chicken/Egg.

Related

Why would layoutIfNeeded be called upon scrolling the uitableviewcontroller?

I'm trying to optimize the UITableViewController for slower devices, and after eliminating many of the reasons why a tableview might be slow, I'm still trying to figure out the last one...
This screenshot is from instruments, showing the most expensive calls, during a time where no call is made to get heightForRowAtIndexPath or cellForRowAtIndexPath (scrolling ~10px up and down repeatedly without causing any cell to go out of the table, or get rendered).
Is this normal behavior? Why would the view need relayout if the content of the page is not changing?
Every time you scroll, you change the contentOffset. contentOffset updates bounds, specifically its origin. Every bounds change marks receiver for layout need in next update cycle. In this case to update frame of floating subviews like scroll indicator, table headers and footers, ... This is totally normal behaviour and there is nothing you can do about it. Don't worry, you are redisplaying, not redrawing.
We'd have to see some code to identify exactly why that method is being called but it could be from one of these reasons at least:
You have constraints that are being changed when scrolling even if you don't see it, they might be affected, maybe inside the UITableViewCell?
It does't have anything to do with the table view, the view just has something else to layout, maybe you made a constraint modification...
Your calling a method thats calls it explicitly.
Also remember this will only layout the view 'if needed'.

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.

Embedded scrollview in tabbar item using storyboards won't scroll

For my current project I wanted to use storyboards and autolayout instead of coding everything by hand. It has gone well so far, but my design has a section of the app where there is a tab bar and one of those tab needs to show four views. The design is to swipe between the four and so I thought to use a scroll view. After some trial and error I found that embedding a Container View in the tab allowed me to easily set up a scroll view and put a couple of view inside it, carefully aligning them using positioning to put them side by side so that each page is one child view. I'm not sure how that plays with the autolayout, and in fact I have the problem that the scroll view won't scroll past the first page position. I can drag about 1/3 of the second page into view, but it never brings that page entirely in view.
I've checked the content size and offset and all of the view positions and it all seems correct. And when I use Spark Inspector to change the scroll offset to the position of the second view/page, the app shows the right page/view and I can even scroll back to the first page. I'm a bit perplexed as to what is causing it to not scroll properly. I don't have any code to show as this is all done in storyboards, but I am wondering if anyone has any ideas as to what is wrong?
Alternately, does anyone have an idea for how to use autolayout and storyboards to set up swiping four adjacent views in a tab? I suspect there are ways to do it. I can think of ways to do it in code, but I'm trying to avoid doing it that way.
EDIT: I set the scroll view delegate to the view controller around it and checked the values of contentSize on scrollViewWillBeginDragging and scrollViewDidScroll. It is always set to {0,0} even after I force set it on viewDidLoad. So I tried setting it every time scrollViewWillBeginDragging is called, which seems work, but I don't understand why this happens and it doesn't smell right. Is there some autolayout constraint that might account for this? Does something cause contentSize get set to {0,0} during the layout process?
For lack of any other answer, I'll use my unsatisfying solution as an answer in the hopes that helps someone else in the future:
I set the scroll view delegate to the view controller around it and checked the values of contentSize on scrollViewWillBeginDragging and scrollViewDidScroll. It is always set to {0,0} even after I force set it on viewDidLoad. So I tried setting it every time scrollViewWillBeginDragging is called, which seems to work.
I'd love to know why the contentSize is being reset if anyone finds out!

Fast custom animation using CADisplayLink causes stroboscopic effect. How to avoid it?

Scenario:
Horizontally scrolling UIScrollView with reused page views (so that there are only few page viewcontrollers which are being reused similar way like UITableView cells). So that they are able to be updated with new content and reused I need to know exact position of UIScrollView's content view (offset). This works great.
Now I need to implement custom scrolling animation - I mean to programatically move the content view, so that user touches some buttons, and the scroll view scrolls to desired position with this custom animation. The movement can be very quick and very far. I can not use Core Animation for this, as I could not track position during animation (CA reports you only start and end of the movement). So I decided to use CADisplayLink, and calculate each UIScrollView content for each position. This works great too.
The only problem is, that sometimes I see stroboscopic effect - say I am moving the content to the right, and it looks like it is moving to left. If I look at builtin animation within UISCrollView using setContentOffset:animated:, the animation is smooth and nice. Does anyone know, how to get rid of this stroboscopic effect?
Most likely your issue is that timestamp is a double and you're assigning it to a float
Floats have 7 digits while Doubles have 15-16 digits.
So in other words you're experiencing data loss. By using double you should see a silky smooth animation.

Strange behavior with horizontal scrolling UITableView (in Monotouch)

We have a UITableView that we have configured to act like a Grid, allowing both horizontal and vertical scrolling. We accomplish this by dynamically changing the ContentSize in a custom UITableView's LayoutSubviews method, which helps with autorotation, scrolling, etc.
Everything works as expected except on a couple of our larger grids. When these grids are scrolled horizontally (swipe left), as soon as the ContentOffset.X is greater than or equal to the Bounds.Width, the table view disappears. It is still present and receives input, but nothing is painted. On swiping back to the right, as soon as the width threshold is crossed, everything is repainted.
In situations where the size of the grid is less than half of the configured UITableView width, this issue does not occur. We cannot change the size of the grids because they are configured by customers in the field who expect the data to be available.
I have checked and/or removed as much of our custom drawing code as possible and the issue is still occurring. Does anyone have any ideas?
That is because although the UITableView inherits from the UIScrollView, it is only meant to have a single column, hence it draws its cells according to its Bounds.
If you want to create a grid-like control with the UITableView, I suggest using multiple table views in a parent UIScrollView side-byside and change that one's ContentSize.
I came across this same issue and posted a bug report (10561166) back in December on the Apple Developer site.
I received a response today from Apple in which they stated that
UITableView is not designed or intended for horizontal scrolling.
(I had since changed my design not to require the horizontal scrolling.)

Resources