I have a UIScrollView with nested UIImageViews. Each imageview can zoom, but when I try to scroll the inner scrollview while zoomed on the image, the outer scrollview picks it up and switches imageviews.
How can I prevent this from happening so that the outer scrollview only scrolls when the inner is not zoomed?
I will post my answer that I got to work to help others out.
One easy way to handle nested UIScrollViews is to share the same delegate. This way, when you detect one UIScrollView scrolling, you can easily share controller logic and apply settings to the other.
to solve this particular problem I was having, All I had to do was keep a BOOL with the current zoom state. Once the app detected that the inner scrollview was zooming,
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView1 {
return [innerScrollViews objectAtIndex:[self indexOfComicViewWithOffset:currentOffset]];
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView1 withView:(UIView *)view atScale:(float)scale {
if (scale == 1) {
zooming = NO;
[outerScrollView setScrollEnabled:YES];
} else {
zooming = YES;
[outerScrollView setScrollEnabled:NO];
}
}
Related
Anyone has an idea how to control two scroll views while one in on the top of the other. One scrolls, the other one scrolls too. Same with zoom, gesture recognisers, etc ... Kinda like passing replica of the touches received by the first view onto the the one underneath. Subclass of the top scroll view has got a weak reference to the "dependant" scroll view underneath. Very important is to get the delegate methods working for both scrollviews as there is a lot of logic in these ...
use the scrollView Delegate methood
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if([scrollView isEqual:scrollViewA]) {
CGPoint offset = scrollViewB.contentOffset;
offset.y = scrollViewA.contentOffset.y;
[scrollViewB setContentOffset:offset];
} else {
CGPoint offset = scrollViewA.contentOffset;
offset.y = scrollViewB.contentOffset.y;
[scrollViewA setContentOffset:offset];
}
}
or simply in the same method for both horizontal an vertical scrolling
if([scrollView isEqual:scrollViewA]) {
scrollViewB.contentOffset = scrollViewA.contentOffset;
}
and viceVersa
I have used two scrollviews,one for horizontal and second for vertical scrolling. I am using scrollViewWillBeginDragging delegate method. When I scroll in horizontal direction, vertical scrollview does not show imageview.
Help me.
Whether or not it is a good idea to nest scrollviews is a different question, but generally you have two scrollviews and check which one is dragging. In your scrollViewWillBeginDragging to the following:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if (scrollView == horizontalScrollView) {
// handle horizontal scrolling
else if (scrollView == verticalScrollView) {
// handle vertical scrolling
}
}
I'm having difficulty though. As soon as i make the content size of the outer scrollview be bigger than it's frame, the image that i'm zooming on it's subview (also a scrollview) is behaving weirdly.
Anybody have any ideas on how to do this? I'm making my own because i have problems with various ones on the internet and i also want it to support video, so i'm making it myself.
FOR ZOOMING inner scrollview you have to right this method.
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
if(scrollView!=Innerscr){
UIImageView *v=(UIImageView*)[scrollView viewWithTag:kTagImageViewInScrollView];
return v;
} else {
return nil;
}
}
and for scrolling images you have to write code in under method
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
if(scrollView==Outerscr){
}
}
Setup: I have a UITableView, each UITableViewCell has a UIScrollView. What I am trying to do is to get all of the UIScrollViews to scroll together, such that when you scroll one of them all of the UIScrollViews appear to scroll simultaneously.
What I've done is subclass UITableView so that it has an array of all of the UIScrollViews within its table cells. I then forwarded TouchesBegan, TouchesMoved, TouchesCancelled, and TouchesEnded from the UITableView to all of the UIScrollViews in the array.
This doesn't appear to work. The UIScrollViews do not scroll! The only way I've managed to get this to work is to call the setContentOffset: method on the scrollviews. However, this is a pretty bad solution since it doesn't give you the swiping and deceleration features of the UIScrollView.
Any ideas on why my touches methods aren't getting to the UIScrollViews? Or a better way to implement this?
Ok, got it working. Thanks for the tips Ricki!
2 things to add to Ricki's solution, if you want to avoid an infinite loop, you have to check to see whether the scrollView's tracking or dragged properties are set. This will insure that only the ScrollView that is actually being dragged is calling the delegate.
- (void)scrollViewDidScroll:(UIScrollView *) theScrollView {
if (theScrollView.dragging || theScrollView.tracking)
[self.delegate scrolling:[theScrollView contentOffSet]];
}
Also, in the scrolling method of the delegate, I set animated to NO, this got rid of the delay between the initial swipe and the other scrollviews getting updated.
I did something "similar" where I had 4 scrollViews incased inside a parent view.
I placed a scrollView inside a UIView, this UIView was passed a delegate from its parentView, that was the view who kept track of all the scrollViews. The UIView containing a scrollVIew implemented the UIScrollViewDelegate and this method;
- (void)scrollViewDidScroll:(UIScrollView *) theScrollView {
[self.delegate scrolling:[self.scrollView contentOffSet]];
}
Now the parent view did this on all the scrollViews:
- (void) scrolling:(CGFloat) offset {
for(UIScrollView *s in self) {
[s setContentOffset:offset animated:YES];
}
}
It is of course a bit of a strain on the CPU, but scrolling several views will be that under any circumstances :/
Hope this was something in the direction of what you needed, and that it made any sense.
Added:
I took me 8 different paths and a lot of mass chaos before I made it work. I dropped the touchedBegan approach early, there is just no way to write something that comes close to Apples swipe, flick, scroll algorithms.
I don't know if the tableview and scrollview will "steal" each others touch events, but as I can read from your description you made that part work.
A follow up idea to ease the CPU usage. add each scrollview to a cell, set its tag=14, now when scrolling asked for all visible cells only, ask for viewWithTag=14, set the contentOffset on this. Save the content offset globally so you can assign it to cells being scrolled onto the screen in cellForRowAtIndexPath.
So set the offSet to a global property, in cellForRowAtIndexPath find the view with tag = 14, set its offset. This way you don't even need a reference to the scrollViews only the delegate.
If you have differently sized UIScrollViews and are using paging, this works great:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)_scrollView {
#pragma unused(_scrollView)
categoryPageControlIsChangingPage = NO;
for (UIImageView *iv in [categoryScrollView subviews]) {
iv.alpha = (iv.tag != categoryPageControl.currentPage+1)?0.5f:1.0f;
ILogPlus(#"%i %i", iv.tag, categoryPageControl.currentPage+1);
}
[self scrolling:_scrollView];
}
- (void)scrolling:(UIScrollView *)sv {
CGFloat offsetX = sv.contentOffset.x;
CGFloat ratio = offsetX/sv.contentSize.width;
if ([sv isEqual:categoryScrollView]) {
[categoryScrollViewLarge setContentOffset:CGPointMake(ratio*categoryScrollViewLarge.contentSize.width, 0) animated:YES];
}else {
[categoryScrollView setContentOffset:CGPointMake(ratio*categoryScrollView.contentSize.width, 0) animated:YES];
}
}
I created a zoomable UIScrollView and added 100 subviews to it (tiled). The view scrolls perfectly left and right. However, I'd like to allow zooming.
To do so I read that my delegate needs to implement:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return ???;
}
I have seen examples that have only one subview to zoom, so they return that subview in that method. In my case, however, I have a lot more. What is the proper way to do the zooming?
I tried creating another UIView and adding the 100 subviews to that one. And then return that one view on the method above, but I doesn't work (it zooms but once it stops, it's not interactive any more).
Exactly,
This is what Apple also is mentioning in the Scroll View Programming Guide:
Just create a view, add all subviews to this view and add the newly created view as a single subview to the scrollview...
Then in the viewForZoomingInScrollView delegate method return the object at Index 0:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return [self.scrollView.subviews objectAtIndex:0];
}
I created the view where I added everything using:
UIView *zoomableView = [[UIView alloc] init];
without setting its frame.
The problem was solved when, after adding all the subviews to it, I set its frame to something large enough to accommodate all the tiled subviews.
You have to return what your going to add views to scroll view as a subviews.
Ex: If you are adding image view to scroll view then write
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return imageView object;
}