UIScrollView scrolling only with one finger - ios

iOS7 & iOS8
I need to disable 2 or three fingers scrolling in UIScrollview.
I tried :
[self.scrollView.panGestureRecognizer setMaximumNumberOfTouches:1];
[self.scrollView.panGestureRecognizer setMinimumNumberOfTouches:1];
But it has no effect. It is still possible to scroll with 2 fingers.
If i tried to set max and min to 2. One finger scrolling was disabled but 3 fingers scrolling possible :(
I tried this too, but without success:
for (UIGestureRecognizer* pan in self.scrollView.gestureRecognizers) {
OTTNSLog(#"touches: %ld", (unsigned long)pan.numberOfTouches);
if ([pan isKindOfClass:[UIPanGestureRecognizer class]])
{
UIPanGestureRecognizer *mpanGR = (UIPanGestureRecognizer *) pan;
mpanGR.minimumNumberOfTouches = 1;
mpanGR.maximumNumberOfTouches = 1;
}
if ([pan isKindOfClass:[UISwipeGestureRecognizer class]])
{
UISwipeGestureRecognizer *mswipeGR = (UISwipeGestureRecognizer *) pan;
mswipeGR.numberOfTouchesRequired = 1;
}
}
Does anybody know, how it solve this ?
Thanks.

PROBLEM:
When the UIPanGestureRecognizer is underlying a UIScrollView - which unfortunately does also effect UIPageViewController - the maximumNumberOfTouches is not behaving as expected, the minimumNumberOfTouches however always limits the lower end correctly.
When monitoring these parameters they report back correct values - they seem to do their job - it's just that UIScrollView itself doesn't honor them and ignores their settings!
SOLUTION:
Set the minimumNumberOfTouches to the desired value e.g. 1 and - very importantly - the maximumNumberOfTouches to 2 !!!
myScrollView.panGestureRecognizer.minimumNumberOfTouches = 1;
myScrollView.panGestureRecognizer.maximumNumberOfTouches = 2;
Conform to the UIGestureRecognizerDelegate protocol in your scrollView's #interface declaration. You don't have to set the panGestureRecognizer.delegate for a UIScrollView!!! The delegate is already set because UIScrollView requires to be the delegate of its own pan/pinchGestureRecognizer.
Then implement the UIGestureRecognizer delegate method:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"%d", gestureRecognizer.numberOfTouches);
NSLog(#"%#", gestureRecognizer.description);
if (gestureRecognizer == self.panGestureRecognizer) {
if (gestureRecognizer.numberOfTouches > 1) {
return NO;
} else {
return YES;
}
} else {
return YES;
}
}
}
AN EVEN SAFER VERSION:
If you have a custom scrollView class and wanna be on the VERY safe side you can also add one more line of code to disambiguate against other scrollViews:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"%d", gestureRecognizer.numberOfTouches);
NSLog(#"%#", gestureRecognizer.description);
if ([gestureRecognizer.view isMemberOfClass:[MY_CustomcrollView class]]) {
if (gestureRecognizer == self.panGestureRecognizer) {
if (gestureRecognizer.numberOfTouches > 1) {
return NO;
} else {
return YES;
}
} else {
return YES;
}
} else {
return YES;
}
}
ADDITIONAL INFORMATION:
The NSLogs tell you the number of touches. If you set both the min and max to the same value (like 1 in the example above) the if-loop would never be triggered... ;-)
That is why maximumNumberOfTouches has to be at least minimumNumberOfTouches + 1
panGestureRecognizer.minimumNumberOfTouches = 1;
panGestureRecognizer.maximumNumberOfTouches = minimumNumberOfTouches + 1;
GEEK SECTION:
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
NSLog(#"myPageViewController - SCROLLVIEW GESTURE RECOGNIZERS: %#", view.gestureRecognizers.description);
((UIPanGestureRecognizer *)view.gestureRecognizers[1]).minimumNumberOfTouches = 1;
((UIPanGestureRecognizer *)view.gestureRecognizers[1]).maximumNumberOfTouches = 2;
}
}
This is the way to access the underlying scrollView that is responsible for the paging of a UIPageViewController. Put this code in the e.g. viewDidLoad: of the UIPageViewController (self).
If you don't have access to the scrollView at all - like for a UIPageViewController in a dynamic UITableViewCell where creation and cell reuse happens at runtime and no outlets can be set on its contentViews - put a category on UIScrollView and override the delegate method there. But be careful! This effects every scrollView in your application - so do proper introspection (class-checking) like in my 'EVEN SAFER' example above... ;-)
SIDENOTE:
Unfortunately the same trick doesn't work with the pinchGestureRecognizer on UIScrollView because it doesn't expose a min/maxNumberOfTouches property. When monitored it always reports 2 touches (which you obviously need to pinch) - so its internal min/maxNumberOfTouches seem to have been set both to 2 - even if UIScrollView isn't honoring its own settings and keeps happily pinching with any numbers of fingers (more than 2). So there is no way to restrict pinching to a limited amount of fingers...

I can confirm this is still an issue in iOS 8, but only when the UIPanGestureRecognizer is underlying a UIScrollView. Creating a UIView with a fresh UIPanGestureRecognizer and setting its maximumNumberOfTouches property works as expected.
Interesting note: if you query the UIScrollView's UIPanGestureRecognizer while you're scrolling, it reports the number of touches as less than or equal to the maximum. In other words, if you set the maximum to 2, and scroll with 3 fingers, it reports the gesture as a 2-finger scroll. Subsequently letting up fingers one at a time while continuing to scroll usually (but not consistently) reduces the reported number of touches as well — so if you go from 3 -> 2 -> 1 fingers, it will register 2 -> 1 -> 0 touches, and stop scrolling while you still have 1 finger on the device.
Submitted rdar://20890684 and copied to http://openradar.appspot.com/radar?id=6191825677189120. Please feel free to dupe.

There is no specific method available for this just do some tricks but results are only 75%.
Add swipe gestures(4 directions each) and double tap gesture to your UIScrollview..Then use below code..
[[yourScrollView panGestureRecognizer] requireGestureRecognizerToFail:swipeDown];
[[yourScrollView panGestureRecognizer] requireGestureRecognizerToFail:swipeLeft];
[[yourScrollView panGestureRecognizer] requireGestureRecognizerToFail:swipeRight];
[[yourScrollView panGestureRecognizer] requireGestureRecognizerToFail:swipeUp];
[[yourScrollView panGestureRecognizer] requireGestureRecognizerToFail:doubleTap];

This will do the job for you:
yourscrollView.multipleTouchEnabled=NO;

Related

WKWebView zooming not working properly

I am using XWalkView which is a subclass of WKWebView, added as a subview to a container view programmatically. I am trying to zoom the content page of the WKWebView but it does not zoom first time. The WKWebView zooms the content page subsequently, i.e. when I pinch zoom or do double tap zoom it zooms WKWebView itself or container view (my guess) & after that, doing zoom gestures again the content page is zoomed (which is expected behaviour).
The question is that how to make the content page of WKWebView zoom whenever the zoom gesture is performed.
I am conforming to UIScrollViewDelegate protocol in my ViewController.h as below:
#interface ViewController : UIViewController<UIScrollViewDelegate, WKNavigationDelegate, WKUIDelegate>
I am setting the delegate to self in viewDidLoad as below:
xWalkView.scrollView.delegate = self;
Setting zooming scales in viewDidLoad as below:
xWalkView.scrollView.minimumZoomScale = 1;
xWalkView.scrollView.maximumZoomScale = 6;
& implementing the protocol methods in ViewController.m as below:
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
if (shouldPageNotZoom) {
NSLog(#"viewForZoomingInScrollView YES");
return nil;
} else {
NSLog(#"viewForZoomingInScrollView NO");
return xWalkView.scrollView.subviews[0];
}
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
}
I am using a BOOL shouldPageNotZoom & setting it YES or NO based on URL depending on whether zooming is required or not. On pages where zooming is required I can see the log of else block (NSLog(#"viewForZoomingInScrollView NO");) being printed in log area & on pages where zooming is not required I can see the log of if block (NSLog(#"viewForZoomingInScrollView YES");). Which is working. The only problem is content page does not zoom on first zooming gesture instead WKWebView or container view is zoomed (I guess), but zooms on subsequent zoom gestures.
After some observation I found that subview at index 0 of scrollview is being zoomed the first & subsequent times (but the content page zooms subsequent times not the first time), which is being returned in viewForZoomingInScrollView. Which should be the case, & is. I did this by assigning the tag values to each view & seeing the tag value by logging the tag value in scrollViewDidEndZooming method in log area as below:
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
if (shouldPageNotZoom) {
NSLog(#"viewForZoomingInScrollView YES");
return nil;
} else {
NSLog(#"viewForZoomingInScrollView NO");
xWalkView.scrollView.tag = 20;
xWalkView.tag = 15;
xWalkView.scrollView.subviews[0].tag = 10;
_containerView.tag = 5;
return xWalkView.scrollView.subviews[0];
}
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
NSLog(#"tag %li", (long) view.tag);
}
Giving the below log:
2017-11-28 17:21:36.610753+0530 MyApp[4614:241659] tag 10 //Getting this 1st time of zooming
2017-11-28 17:21:59.369461+0530 MyApp[4614:241659] tag 10 //Getting this at subsequent zooms
I don't know what am I missing.
Please comment if any clarification is needed.
Got the solution! Returning the last subview first time & first subview of scrollview on subsequent zooms in viewForZoomingInScrollView method solved the problem, as shown below:
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
if (shouldPageNotZoom) {
NSLog(#"viewForZoomingInScrollView YES");
return nil;
} else {
NSLog(#"viewForZoomingInScrollView NO");
xWalkView.scrollView.tag = 20;
xWalkView.tag = 15;
xWalkView.scrollView.subviews[0].tag = 10;
_containerView.tag = 5;
if (isFirstTimeZooming) {
return xWalkView.scrollView.subviews[xWalkView.scrollView.subviews.count - 1];
} else {
return xWalkView.scrollView.subviews[0];
}
}
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
NSLog(#"tag %li", (long) view.tag);
isFirstTimeZooming = NO;
}
isFirstTimeZooming is set to YES on load of the page in didStartProvisionalNavigation method & set to NO in scrollViewDidEndZooming method so that first subview of scrollview can be passed on subsequent zooming.
I had to use isFirstTimeZooming because if I pass last subview only in viewForZoomingInScrollView the zooming problem was reversed, i.e. it was zooming properly first time but not on subsequent zooms.

Interactive transition like Google Maps (iOS)

I want to achieve something very close to what Google Maps (iOS) does and I have some doubts.
But first, a better explanation and things to take into account:
-------------- ANSWER --------------
Thanks to Jugale's contribution, here's a repository so everybody can download and test everything out.
https://github.com/vCrespoP/VCSlidingView
-------------- ORIGINAL QUESTION -----------
You tap in a point inside the map, a view comes in from the bottom but then when you interact with that summary view:
Notice when pulling just a bit, the navigation bar already has set.
When you have scrolled it to the top, you can continue scrolling and the inner scrollview will continue scrolling.
When you 'reverse' the action to dismiss the view, the PanGesture doesn't mess up with the inner scrollView (same for the other way, scrollView VS dismiss)
Here it is in action:
Doubts:
I've tried to do it as an interactive transition (UIPercentDrivenInteractiveTransition) and separating Map from Details in 2 controllers but I'm having troubles with the UIPanGesture interfering with the scrollView.
Maybe it's better to do it as a subview and handle everything there? More or less like MBPullDownController (Although it has some issues with iOS8) -> https://github.com/matej/MBPullDownController
So, anybody knows any framework, has done it, or knows how to do this in a good way?
Thank you for your time :D
Looking through my implementation it seems the following are true:
I have a subclass of UIViewController that is the view controller
I have a subclass of UIView that is the overlay (and henceforth with the known as "the overlay") (actually for me this is a UIScrollView because it needs to go sideways too, but I'll try and filter out the unnecessary code)
I have another subclass of UIView that loads the overlay's content ("the content wrapper")
The content wrapper has a UIScrollView property, in which all other views are loaded ("the content view")
The view controller is responsible for initializing the overlay, setting it's initial frame (where the height is the height of the screen) and passing content to it, nothing more.
From it's -initWithFrame method, the overlay sets itself up with a UIDynamicItemBehavior. It also creates some UICollisionBehavior objects: one at the top of the screen and one below the bottom of the screen at just the right y position for the top of the overlay to be partially visible (as seen in the first frame of your GIF). A UIGravityBehavior is also set up to keep the overlay sitting on the lower collision boundary. Of course, _animator = [[UIDynamicAnimator alloc... is set up too.
Finally:
_pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan)];
_pan.delegate = self;
_pan.cancelsTouchesInView = FALSE;
The overlay class also has some other helpful methods such as changing the gravity's direction so that the overlay can appear to snap to the top or bottom of the screen.
The _pan handler uses a UISnapBehavior to keep the overlay moving dynamically up and down the screen underneath the user's finger:
- (void)handlePan
{
[self handlePanFromPanGestureRecogniser:_pan];
}
- (void)handlePanFromPanGestureRecogniser:(UIPanGestureRecognizer *)pan
{
CGFloat d = [pan velocityInView:self.superview.superview].y;
CGRect r = self.frame;
r.origin.y = r.origin.y + (d*0.057);
if (r.origin.y < 20)
{
r.origin.y = 20;
}
else if (r.origin.y > [UIScreen mainScreen].bounds.size.height - PEEKING_HEIGHT)
{
r.origin.y = [UIScreen mainScreen].bounds.size.height - PEEKING_HEIGHT;
}
if (pan.state == UIGestureRecognizerStateEnded)
{
[self panGestureEnded];
}
else if (pan.state == UIGestureRecognizerStateBegan)
{
[self snapToBottom];
[self removeGestureRecognizer:_tap];
}
else
{
[_animator removeBehavior:_findersnap];
_findersnap = [[UISnapBehavior alloc] initWithItem:self snapToPoint:CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r))];
[_animator addBehavior:_findersnap];
}
}
- (void)panGestureEnded
{
[_animator removeBehavior:_findersnap];
CGPoint vel = [_dynamicSelf linearVelocityForItem:self];
if (fabsf(vel.y) > 250.0)
{
if (vel.y < 0)
{
[self snapToTop];
}
else
{
[self snapToBottom];
}
}
else
{
if (self.frame.origin.y > (self.superview.bounds.size.height/2))
{
[self snapToBottom];
}
else
{
[self snapToTop];
}
}
}
The content wrapper listens for scroll events generated by the content view:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//this is our fancy way of getting the pan to work when the scrollview is in the way
if (scrollView.contentOffset.y <= 0 && _dragging)
{
_shouldForwardScrollEvents = TRUE;
}
if (_shouldForwardScrollEvents)
{
if ([_delegate respondsToSelector:#selector(theContentWrapper:isForwardingGestureRecogniserTouches:)])
{
[_delegate theContentWrapper:self isForwardingGestureRecogniserTouches:scrollView.panGestureRecognizer];
}
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
_dragging = FALSE;//scrollviewdidscroll must not be called after this
if (scrollView.contentOffset.y <= 0 || _shouldForwardScrollEvents)
{
if ([_delegate respondsToSelector:#selector(theContentWrapperStoppedBeingDragged:)])
{
[_delegate theContentWrapperStoppedBeingDragged:self];
}
}
_shouldForwardScrollEvents = FALSE;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
_dragging = TRUE;
}
As you can see, when the bool shouldForwardScrollEvents is TRUE then we send scrollView.panGestureRecognizer to the content wrapper's delegate (the overlay). The overlay implements the delegate methods like so:
- (void)theContentWrapper:(TheContentWrapper *)contentWrapper isForwardingGestureRecogniserTouches:(UIPanGestureRecognizer *)contentViewPan
{
[self handlePanFromPanGestureRecogniser:contentViewPan];
}
- (void)theContentWrapperStoppedBeingDragged:(TheContentWrapper *)contentWrapper
{
//because the scrollview internal pan doesn't tell use when it's state == ENDED
[self panGestureEnded];
}
Hopefully at least some of this is useful to someone!

Swipe UILabel to alpha 0

I have a label, which I want to "swipe to delete". What I want is:
If the user touches the label, and starts dragging it to the right, the label is moving along with the finger
The more the label moves to the right, the less alpha it gets
When it reaches alpha 0, fire a method.
Any idea what the best way to implement this is?
Thanks in advance!
You'll want to use a UIPanGestureRecognizer to do this. When the gesture recognizer starts, you'll want to keep track of the starting point. You'll also want to define some amount to pan over that is considered all the way over. As the pan happens, you'll see how far over your touch has moved and move the label by that amount. You'll also determine what percentage of the way over that you've moved thru your "all the way over" distance, and set the alpha accordingly.
Once you reach the spot, you can cancel the gesture recognizer (setting its enabled property to NO) and do whatever you want to do at that point. If the user releases their touch (so the gesture recognizer ends before they drag all of the way) you'll obviously want to reset the label position and alpha at that point.
You may also want to take into account the velocity of the pan at the time it ends, and if it's over a certain velocity, go ahead and make it continue animating to the finished state at that velocity, otherwise if it's not fast enough, make it animate back to the starting state. But you may want to only bother with this after you initially implement it to see if you want this or not.
Put this in your UIViewController.
WARNING: I typed all this without XCode and never tested it. You may need to fix spelling errors and adjust numbers.
// Declare this in the anonymous category of your UIViewController
#property(nonatomic, strong) UILabel* label;
- (void)didSwipeLabel:(UISwipeGestureRecognizer*)swipe;
- (void)willRemoveLabel;
// Put the following in the usual places in .m file
- (void)viewDidLoad {
[super viewDidLoad];
self.label = [UILabel alloc] init];
self.label.font = [UIFont boldSystemFontOfSize:30.0];
self.label.text = #"SWIPE THIS LABEL TO CHANGE THE ALPHA";
[self.label sizeToFit];
[self.label addGestureRecognizer:[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipeLabel:)]];
[self.view addSubview:self.label];
}
- (void)didSwipeLabel:(UISwipeGestureRecognizer*)swipe
{
// The value of 0.1 needs to be adjusted. Most likely it needs
// to be decreased.
if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
self.label.alpha = self.label.alpha - 0.1;
} else if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
self.label.alpha = self.label.alpha + 0.1;
}
if (self.label.alpha <= 0.0) {
[self willRemoveLabel];
}
}
- (void)willRemoveLabel
{
NSLog(#"label should be removed");
}

Some buttons fail hitTest

My interface sometimes has buttons around its periphery. Areas without buttons accept gestures.
GestureRecognizers are added to the container view, in viewDidLoad. Here’s how the tapGR is set up:
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(playerReceived_Tap:)];
[tapGR setDelegate:self];
[self.view addGestureRecognizer:tapGR];
In order to prevent the gesture recognizers from intercepting button taps, I implemented shouldReceiveTouch to return YES only if the view touched is not a button:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gr
shouldReceiveTouch:(UITouch *)touch {
// Get the topmost view that contains the point where the gesture started.
// (Buttons are topmost, so if they were touched, they will be returned as viewTouched.)
CGPoint pointPressed = [touch locationInView:self.view];
UIView *viewTouched = [self.view hitTest:pointPressed withEvent:nil];
// If that topmost view is a button, the GR should not take this touch.
if ([viewTouched isKindOfClass:[UIButton class]])
return NO;
return YES;
}
This works fine most of the time, but there are a few buttons that are unresponsive. When these buttons are tapped, hitTest returns the container view, not the button, so shouldReceiveTouch returns YES and the gestureRecognizer commandeers the event.
To debug, I ran some tests...
The following tests confirmed that the button was a sub-subview of the container view, that it was enabled, and that both button and the subview were userInteractionEnabled:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gr
shouldReceiveTouch:(UITouch *)touch {
// Test that hierarchy is as expected: containerView > vTop_land > btnSkipFwd_land.
for (UIView *subview in self.view.subviews) {
if ([subview isEqual:self.playComposer.vTop_land])
printf("\nViewTopLand is a subview."); // this prints
}
for (UIView *subview in self.playComposer.vTop_land.subviews) {
if ([subview isEqual:self.playComposer.btnSkipFwd_land])
printf("\nBtnSkipFwd is a subview."); // this prints
}
// Test that problem button is enabled.
printf(“\nbtnSkipFwd enabled? %d", self.playComposer.btnSkipFwd_land.enabled); // prints 1
// Test that all views in hierarchy are interaction-enabled.
printf("\nvTopLand interactionenabled? %d", self.playComposer.vTop_land.userInteractionEnabled); // prints 1
printf(“\nbtnSkipFwd interactionenabled? %d", self.playComposer.btnSkipFwd_land.userInteractionEnabled); // prints 1
// etc
}
The following test confirms that the point pressed is actually within the button’s frame.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gr
shouldReceiveTouch:(UITouch *)touch {
CGPoint pointPressed = [touch locationInView:self.view];
CGRect rectSkpFwd = self.playComposer.btnSkipFwd_land.frame;
// Get the pointPressed relative to the button's frame.
CGPoint pointRelSkpFwd = CGPointMake(pointPressed.x - rectSkpFwd.origin.x, pointPressed.y - rectSkpFwd.origin.y);
printf("\nIs relative point inside skipfwd? %d.", [self.playComposer.btnSkipFwd_land pointInside:pointRelSkpFwd withEvent:nil]); // prints 1
// etc
}
So why is hitTest returning the container view rather than this button?
SOLUTION: The one thing I wasn't testing was that the intermediate view, vTop_land, was framed properly. It looked OK because it had an image that extended across the screen -- past the bounds of its frame (I didn't know this was possible). The frame was set to portrait width, rather than landscape width, so buttons on the far right were out of zone.
Hit test is not reliable in most cases, and it is generally not advisable to use it along with gestureRecognizers.
Why dont you setExclusiveTouch:YES for each button, and this should make sure that the buttons are always chosen.

UIScrollView detect pinch zoom end

I'm trying to get notified when UIScrollView is pinch zoomed out beyond its minimum zoom limit and is about to animate back, but I'm finding it very difficult. Is there a way I can do this with delegate methods alone or do I need to override UIScrollView's touch handling?
Use scrollViewDidZoom: and check if scrollView.zoomBouncing == YES. Then use zoomScale to determine which direction the view is bouncing.
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
if (scrollView.zoomBouncing) {
if (scrollView.zoomScale == scrollView.maximumZoomScale) {
NSLog(#"Bouncing back from maximum zoom");
}
else
if (scrollView.zoomScale == scrollView.minimumZoomScale) {
NSLog(#"Bouncing back from minimum zoom");
}
}
}
You can use UIScrollView's scrollViewDidZoom delegate method to detect the moment it's about to animate back. You'll see scrollView.zoomScale drop below scrollView.minimumZoomScale while the view is being pinched. Then, as soon as the user releases their fingers, scrollViewDidZoom will be called once again with scrollView.zoomScale == scrollView.minimumZoomScale, but scrollView.zooming == NO.
Capturing this moment is fine and all, but attempting to do anything to preempt the bounce-back-to-minimumZoomScale animation seems to have really odd side effects for me. :(
In Swift 4.0:
func scrollViewDidZoom(_ scrollView: UIScrollView) {
if scrollView.zoomScale == scrollView.minimumZoomScale
{
print("zoomed out")
}
}
This will be called exactly when the user has finished zooming and the zoomScale is at its minimum possible value, i.e. when the scroll view is zoomed out completely.
I did it with UIPinchGestureRecognizer.
-(void)viewDidLoad{
UIPinchGestureRecognizer *gestureRecognizer =
[[[UIPinchGestureRecognizer alloc] initWithTarget:self
action:#selector(pinched:)]
autorelease];
gestureRecognizer.delegate=self;
[self.scrollView addGestureRecognizer:gestureRecognizer];
//your code
}
-(void)pinched:(UIPinchGestureRecognizer*)gestureRecognizer{
if(gestureRecognizer.state==UIGestureRecognizerStateEnded){
//pinch ended
NSLog(#"scale: %f",scrollView.zoomScale);
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:
(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}

Resources