Interactive transition like Google Maps (iOS) - 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!

Related

UIScrollView scrolling only with one finger

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;

Continuous vertical scrolling between UICollectionView nested in UIScrollView

While I know nested scrollViews aren't ideal, our designers provided me with this setup, so I'm doing my best to make it work. Let's begin!
View Hierarchy
UIView
UIScrollView (Vertical Scrolling Only)
UIImageView
UICollectionView #1 (Horizontal Scrolling Only)
UIImageView (different from previous UIImageView)
UICollectionView #2 (Vertical Scrolling Only)
Important Note
All my views are defined using programmatic Auto Layout. Each successive view in the UIScrollView's subview hierarchy has a y-coordinate dependency on the view that came before it.
The Problem
For the sake of simplicity, let's modify the nomenclature a bit:
_outerScrollView will refer to UIScrollView
_innerScrollView will refer to UICollectionView #2
I'd like for my _outerScrollView to route its touch event to the _innerScrollView upon reaching the bottom of its contentSize. I'd like the reverse to happen when I scroll back up.
At present, I have the following code:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat bottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);
if (bottomEdge >= [_outerScrollView contentSize].height) {
_outerScrollView.scrollEnabled = NO;
_innerScrollView.scrollEnabled = YES;
} else {
_outerScrollView.scrollEnabled = YES;
_innerScrollView.scrollEnabled = NO;
}
}
where the initial conditions (before any scrolling occurs) is set to:
outerScrollView.scrollEnabled = YES;
innerScrollView.scrollEnabled = NO;
What happens?
Upon touching the view, the outerScrollView scrolls until its bottom edge, and then has a rubber band effect due to _outerScrollView.bounces = YES; If I touch the view again, the innerScrollView scroll until it hits its bottom edge. On the way back up, the same rubber banding effect occurs in the reverse order. What I want to happen is have a fluid motion between the two subviews.
Obviously, this is due to the scrollEnabled conditions that are set in the conditional in the code snippet. What I'm trying to figure out is how to route the speed/velocity of one scrollView to the next scrollView upon hitting an edge.
Any assistance in this issue would be greatly appreciated.
Other Notes
This did not work for me: https://github.com/ole/OLEContainerScrollView
I am considering putting everything in the UIScrollView hierarchy (except for UICollectionView #2) inside UICollectionView #2 supplementaryView. Not sure if that would work.
Figured it out!
First:
_scrollView.pagingEnabled = YES;
Second:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == _scrollView || scrollView == _offersCollectionView) {
CGFloat offersCollectionViewPosition = _offersCollectionView.contentOffset.y;
CGFloat scrollViewBottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);
if (scrollViewBottomEdge >= [_scrollView contentSize].height) {
_scrollView.scrollEnabled = NO;
_offersCollectionView.scrollEnabled = YES;
} else if (offersCollectionViewPosition <= 0.0f && [_offersCollectionView isScrollEnabled]) {
[_scrollView scrollRectToVisible:[_scrollView frame] animated:YES];
_scrollView.scrollEnabled = YES;
_offersCollectionView.scrollEnabled = NO;
}
}
}
Where:
_scrollView is the _outerScrollView
_offersCollectionView is the _innerScrollView (which was UICollectionView #2 in my original post).
Here's what happens now:
When I swipe up (so the view moves down), the offersCollectionView takes over the entire view, moving the other subViews out of the view.
If I swipe down (so the views up), the rest of the subviews come back into focus with the scrollView's bounce effect.
Accepted answer didn't work for me. Here's what did:
Define a subclass of UIScrollView:
class CollaborativeScrollView: UIScrollView, UIGestureRecognizerDelegate {
var lastContentOffset: CGPoint = CGPoint(x: 0, y: 0)
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer.view is CollaborativeScrollView
}
}
Since it's not possible to reroute touches to another view, the only way to ensure that the outer scrollview can continue scrolling once the inner one stops is if it had been receiving the touches the whole time. However, in order to prevent the outer one from moving while the inner one is, we have to lock it without setting its isScrollEnabled to false, otherwise it'll stop receiving the touches and won't be able to pick up where the inner one left off when we want to scroll past the inner one's top or bottom.
That's done by assigning a UIScrollViewDelegate to the scrollviews, and implementing scrollViewDidScroll(_:) as shown:
class YourViewController: UIViewController, UIScrollViewDelegate {
private var mLockOuterScrollView = false
#IBOutlet var mOuterScrollView: CollaborativeScrollView!
#IBOutlet var mInnerScrollView: CollaborativeScrollView!
enum Direction {
case none, left, right, up, down
}
func viewDidLoad() {
mOuterScrollView.delegate = self
mInnerScrollView.delegate = self
mInnerScrollView.bounces = false
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView is CollaborativeScrollView else {return}
let csv = scrollView as! CollaborativeScrollView
//determine direction of scrolling
var directionTemp: Direction?
if csv.lastContentOffset.y > csv.contentOffset.y {
directionTemp = .up
} else if csv.lastContentOffset.y < csv.contentOffset.y {
directionTemp = .down
}
guard let direction = directionTemp else {return}
//lock outer scrollview if necessary
if csv === mInnerScrollView {
let isAlreadyAllTheWayDown = (mInnerScrollView.contentOffset.y + mInnerScrollView.frame.size.height) == mInnerScrollView.contentSize.height
let isAlreadyAllTheWayUp = mInnerScrollView.contentOffset.y == 0
if (direction == .down && isAlreadyAllTheWayDown) || (direction == .up && isAlreadyAllTheWayUp) {
mLockOuterScrollView = false
} else {
mLockOuterScrollView = true
}
} else if mLockOuterScrollView {
mOuterScrollView.contentOffset = mOuterScrollView.lastContentOffset
}
csv.lastContentOffset = csv.contentOffset
}
}
And that's it. This will stop your outer scrollview from scrolling when you begin scrolling the inner one, and get it to pick up again when the inner one is scrolled all the way to one of its ends.

Make a UI scroll view scroll with a UIPageViewController

I am creating an app where I am trying to have a pagescrollviewcontroller swipe through screens and have the top "nav" bar titles scroll real time with the titles
i have the title bar view as a custom view and I am able to access the scroll delegate methods for both the custom scroll view and the pageview controller. However. I don't see how to access the real time scroll pos? I know it is possible because twitter does it (really can't shouldn't be in anyone's vocabulary) but I am not sure how to achieve this.
a picture
the home title swipes at the same scroll pos as the pagecontrollers.
current code:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if(scrollView.tag == 100) {
CGPoint point = CGPointMake(mainScrollView.contentOffset.x * 1,0);
[titleSwipe setContentOffset:point animated:YES];
}
else {
mainScrollView.contentOffset = CGPointMake(0,1);
[mainScrollView setContentOffset:titleSwipe.contentOffset animated:YES];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate: (BOOL)decelerate {
CGPoint point = CGPointMake(mainScrollView.contentOffset.x * 2,0);
[titleSwipe setContentOffset:point animated:YES];
}
You'll find the real time position in scrollViewDidScroll:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// X scroll
//
CGFloat percentage = scrollView.contentOffset.x / scrollView.contentSize.width;
NSLog(#"Scrolled percentage: %f", percentage);
}
Hope that helps!

Pinch To Zoom Effect on UIImageView inside scrollView?

I'm using storyboard (iOS 6.0) to create a photo gallery viewer for my app. This is how my imageViewController is set up in storyboard:
I've made sure to enable userInteraction and multiple touches on both the imageView and scrollView. What I want to do is, on pinch I want to zoom into the imageView (maximum scale 3) and be able to pan around. This is what I currently have, however, even though the pinch gesture is detected the scale does not change.
- (IBAction)imagePinched:(id)sender {
if (pinchRecognizer.state == UIGestureRecognizerStateEnded || pinchRecognizer.state == UIGestureRecognizerStateChanged) {
NSLog(#"gesture.scale = %f", pinchRecognizer.scale);
CGFloat currentScale = self.fullScreenView.frame.size.width / self.fullScreenView.bounds.size.width;
CGFloat newScale = currentScale * pinchRecognizer.scale;
if (newScale < 1) {
newScale = 1;
}
if (newScale > 3) {
newScale = 3;
}
CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
self.fullScreenView.transform = transform;
pinchRecognizer.scale = 1;
}
}
Most questions and tutorials online deal with programmatically creating the views and doing this, but the less code the better (in my eyes). What's the best way to get this to work with storyboard? Thank you in advance!!!
UPDATED:
Here is my full .m file code:
- (void)viewDidLoad
{
[super viewDidLoad];
//Assign an image to this controller's imageView
fullScreenView.image = [UIImage imageNamed:imageString];
//Allows single and double tap to work
[singleTapRecognizer requireGestureRecognizerToFail: doubleTapRecognizer];
}
- (IBAction)imageTapped:(id)sender {
NSLog(#"Image Tapped.");
//On tap, fade out viewController like the twitter.app
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)imageDoubleTapped:(id)sender {
NSLog(#"Image Double Tapped.");
//On double tap zoom into imageView to fill in the screen.
[fullScreenView setContentMode:UIViewContentModeScaleAspectFill];
}
- (IBAction)imagePinched:(id)sender {
if (pinchRecognizer.state == UIGestureRecognizerStateEnded || pinchRecognizer.state == UIGestureRecognizerStateChanged) {
NSLog(#"gesture.scale = %f", pinchRecognizer.scale);
CGFloat currentScale = self.fullScreenView.frame.size.width / self.fullScreenView.bounds.size.width;
CGFloat newScale = currentScale * pinchRecognizer.scale;
if (newScale < 1) {
newScale = 1;
}
if (newScale > 3) {
newScale = 3;
}
CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
self.fullScreenView.transform = transform;
pinchRecognizer.scale = 1;
}
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.fullScreenView;
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I think better solution in Apple Documentation
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView.minimumZoomScale=0.5;
self.scrollView.maximumZoomScale=6.0;
self.scrollView.contentSize=CGSizeMake(1280, 960);
self.scrollView.delegate=self;
}
Check Apple Documentation
The first step is to make sure your views have the correct delegates implemented. For example in the .m file
#interface myRootViewController () <.., UIGestureRecognizerDelegate, UIScrollViewDelegate, ...>
From the documentation, make sure you have this implemented:
The UIScrollView class can have a delegate that must adopt the UIScrollViewDelegate protocol. For zooming and panning to work, the delegate must implement both viewForZoomingInScrollView: and scrollViewDidEndZooming:withView:atScale:; in addition, the maximum (maximumZoomScale) and minimum ( minimumZoomScale) zoom scale must be different.
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.fullScreenView;
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{}
The scrollViewDidEndZooming method can stay empty for now. If you already did that or it still doesnt work, please post more of your code and then it is easier to help more specifically.
You need to do what Wadi suggested above but also set the setMinimumZoomScale to be different from setMaximumZoomScale. They are both 1.0f by default.
After that, the UIImageView should be pinchable
I have created a fully working demo application (similar in nature to Facebook's default photo gallery) which demonstrates pinch to zoom of Image View nested inside a scroll view with AutoLayout and storyboards. View my project here: http://rexstjohn.com/facebook-like-ios-photo-modal-gallery-swipe-gestures/.
It also includes async photo loading via MKNetworkKit and gesture based swiping through a photo gallery. Enjoy and I hope it saves everyone time trying to figure this out because it is somewhat annoying.
Here is how Apple recomends to do what you want to do. I used the code provided by Apple and it worked like a charm! If you want to optimize memory management, you can use iCarousel (made by nicklockwood), which have a cache management for dealloc unused views!
I've created a class to handle zooming of UIImageViews similar to Instagram. Might be what you're looking for, otherwise you can look at how I achieved it. https://github.com/twomedia/TMImageZoom

UIScrollView - how is showsHorizontalScrollIndicator calculated?

If showsHorizontalScrollIndicator is enabled, a small horizontal indicator is displayed at the bottom of the UIScrollView. I was wondering if anyone could tell me how this is calculated. The reason being, I want to display a custom indicator in a horizontal navigation system if there is content left/right, show/hiding when content is available.
I just use scrollView.contentOffset.x. If you want to know what page you are on, you divide by your page width (assuming you have a standard width). So
int currentPage=(int)scrollView.contentOffset.x/SCREEN_WIDTH; for example. Now if you want to display a position indicator, like in the iOS homepage, you can do it in a loop in
-(void)scrollViewDidScroll:(UIScrollView *)sender something like
currentPage=(int)scrollView.contentOffset.x/SCREEN_WIDTH;
for(int i=0; i<pageCount; i++){
if(i==currentPage)//display a filled in 'o'
else //display an empty 'o'
}
Oh, BTW, -(void)scrollViewDidScroll:(UIScrollView *)sender is not called when the UIScrollView is initially loaded, so you perhaps should call it yourself (in order to display the indicator before the user has scrolled). You can also use scrollView.contentOffset.y off course, so you could easily adapt this to show a '+' shaped arrangement of 'o's, hinting at content above and below also.
I used the following solution. Not the prettiest, but it seems to work. Requires UIScrollViewDelegate.
-(void)scrollViewDidScroll:(UIScrollView *)sender
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self scrollViewDidEndScrollingAnimation:subNavigation];
}
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
if (scrollView.contentOffset.x <= 0) {
[self scrollViewReachedLeftBound];
} else if (scrollView.contentOffset.x >= 300) {
[self scrollViewReachedRightBound];
} else {
[leftArrow setHidden:NO];
[rightArrow setHidden:NO];
}
}
- (void)scrollViewReachedLeftBound
{
[leftArrow setHidden:YES];
[rightArrow setHidden:NO];
}
- (void)scrollViewReachedRightBound
{
[leftArrow setHidden:NO];
[rightArrow setHidden:YES];
}

Resources