UIScrollView fades out while dragging the last page - ios

I'm developing an iOS 7 app with latest SDK.
I have an UIScrollView with three ImageViews inside it. The UIScrollView has its contentSize set to {960, 568} (each ImageView has a {320, 958}` size).
I can drag over the three images perfectly, but I want to hide the UIScrollView when user drags from right to left on the last image.
To that, I have used UIScrollViewDelegate:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x > 640)
scrollView.hidden = YES;
}
But this code hide it immediately and I want to show the following effect:
Continue dragging the image from right to left while the image fades out.
How can I do that?

If you want to have the fade effect proportional to the content offset, use this method:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x > 640)
{
scrollView.alpha = 1 / (scrollView.contentOffset.x - 639);
}
// then remove / hide the view completely at a particular offset
// ....
}
and you can change the division to make the animation more/less smooth. This is smoother:
scrollView.alpha = 2 / (scrollView.contentOffset.x - 638);

[UIView animateWithDuration:0.3 animations:^{
self.scrollView.alpha = 0.0;
} completion:^(BOOL finished) {
// remove the scrollView from your view
if(finished) {
[self.scrollView removeFromSuperview];
}
}];
Hopefully this will help

Related

Animate constraint change smoothly with pan gesture

How to animate constraint change smoothly with pan gesture in iOS?
I am trying to develop a screen, where a view is at bottom of the screen. And I've added pan gesture to that view. On dragging that view I want change top constraint of that view. Pan gesture is only allowed in vertical and downward direction. I have added some limit for dragging the view. It working but not smoothly. How to animate constraint change smoothly with pan gesture? Here is my code.
- (void)handleGesture:(UIPanGestureRecognizer *)sender
{
CGPoint velocity = [sender velocityInView:_locationContainer];
[sender setTranslation:CGPointMake(0, 0) inView:self.view];
if (fabs(velocity.y) > fabs(velocity.x)) {
NSLog(#"velocity y %f ",velocity.y * 0.13);
if(velocity.y < 0 && (self.locationDetailsTop.constant > minimumTop) )
{
NSLog(#"gesture moving Up");
self.locationDetailsTop.constant = self.locationDetailsTop.constant - fabs(velocity.y * 0.1);
}
else if (self.locationDetailsTop.constant < firstTop)
{
NSLog(#"gesture moving Bottom");
self.locationDetailsTop.constant = self.locationDetailsTop.constant + fabs(velocity.y * 0.1);
}
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.1 animations:^{
[self.mapView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.locationContainer.frame.origin.y)];
}];
}
}
This is sample image,
My screen is of the same kind like this, But on my screen, there is a map view instead of the calender view
To move view while user is touching a screen, you can use translationInView: property. You can set a translation to current constraint's value and get new value (in a handler of UIGestureRecognizerStateBegan) and change a constraint's constant in a handler of UIGestureRecognizerStateChanged:
- (void)padRecognizerStateChanged:(UIPanGestureRecognizer*)sender
{
if(sender.state == UIGestureRecognizerStateBegan)
{
[sender setTranslation:CGPointMake(0.0, [self getConstraintValue]) inView: _locationContainer];
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
[self setConstraintValue: [sender translationInView:_locationContainer].y];
[self.view setNeedsLayout];
}
}
You can use velocity if you need to move view when a user raised his thumb over the screen for upward or downward movement. For example, you can implement deceleration effect.
If you want it to animate smoothly, try calling layoutIfNeeded inside the animation block like so:
[UIView animateWithDuration:0.1 animations:^{
[self.view layoutIfNeeded];
[self.mapView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.locationContainer.frame.origin.y)];
}];
I think the original question was asking how to perform this animation smoothly. However, if the map view constraints are linked to the dragged view constraints, due to the polynomial relationship between autolayout constraints and laying out the MKMapView the computation will be quite intense and therefore there will likely be lag. I suggest disconnecting the map constraints from the dragged view constraints if the UI/UX design allows.

IOS/Objective-C: Slide updated view in from right on swipe

I would like to emulate the swipe to left to show next picture seen in the photos app, among other places so that the new picture slides in from right to left using animation.
However, in my case, the view has more than one photo. It has a caption as well.
I am able to do this without animation by just updating the photo and text upon swipe. This just changes the photo and caption on screen, however, without the right to left animation technique.
Is there a way to show the old view moving to the left while the new updated view moves in from the right.
The following moves the view to the left but renders the screen black as nothing is put in its place.
//In handleSwipe called by gesture recognizer
CGRect topFrame = self.view.frame;
topFrame.origin.x = -320;
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.view.frame = topFrame; _myImage=newImage;
_mycaption=newcaption;} completion:^(BOOL finished){ }];
Thanks for any suggestions.
Edit:
The view in question is a detail view that you get to from a tableview through a segue. It currently already uses a scrollview for vertical scrolling as the content can exceed a single screen in the vertical dimension.
I have found a way to get the old photo and caption to move left and the new to come in from right using a completion block, however, it is less than satisfactory, because there is a gap between the two actions when the screen is black. This seems endemic to completion approach because new image does not start to move until old image has completed moving.
- (IBAction)swipedLeft:(id)sender
{
CGRect initialViewFrame = self.view.frame;
CGRect movedToLeftViewFrame = CGRectMake(initialViewFrame.origin.x - 375, initialViewFrame.origin.y, initialViewFrame.size.width, initialViewFrame.size.height);
CGRect movedToRightViewFrame = CGRectMake(initialViewFrame.origin.x + 375, initialViewFrame.origin.y, initialViewFrame.size.width, initialViewFrame.size.height);
[UIView animateWithDuration:0.1
animations:^{self.view.frame = movedToLeftViewFrame;
//above moves old image out of view.
}
completion:^(BOOL finished)
{
self.view.frame = movedToRightViewFrame;
[self loadNextItem];//changes pic and text.
[UIView animateWithDuration:0.1
animations:^{self.view.frame = initialViewFrame;
//moves new one in
}
completion:nil];
}];
}
I suggest using a UIPageViewController, and manage each image/set of images as a separate view controller.
A page view controller supports either a page curl style transition or a slide transition. You'd want the slide transition.
Three ways to build this using existing components:
UIPageViewController (see DuncanC's answer).
UICollectionView with pagingEnabled set to true. Use a full-screen-sized cell and a UICollectionViewFlowLayout with scrollDirection set to horizontal.
UICollectionView as above, but instead of setting pagingEnabled to true, implement scrollViewWillEndDragging:withVelocity:targetContentOffset: in your delegate. This allows you to have a gutter between each cell while still having each cell fill the screen. See this answer.
It's ScrollView+PageControl
I hope it can help you.
- (void)viewDidLoad {
    [super viewDidLoad];
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.delegate =self;
    self.scrollView.translatesAutoresizingMaskIntoConstraints =NO;
    self.scrollView.pagingEnabled =YES;
 self.scrollView.contentSize =CGSizeMake(ViewWidth*VIewCount, ViewHeight);
    [self.view addSubview:self.scrollView];
 self.pageControl = [[UIPageControl alloc] init];
   self. pageControl.translatesAutoresizingMaskIntoConstraints =NO;
    [self.view addSubview:self.pageControl];
    self. pageControl.numberOfPages = VIewCount;
    self.pageControl.currentPage = 0;
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView
                                                           attribute:NSLayoutAttributeCenterX
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:self.view
                                                           attribute:NSLayoutAttributeCenterX
                                                          multiplier:1.0f
                                                            constant:0.0f]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[self.scrollView(width)]"
                                                                     options:0
                                                                     metrics:#{#"width":#(ViewWidth)}
                                                                       views:NSDictionaryOfVariableBindings(self.scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[self.scrollView(height)]"
                                                                      options:0
                                                                      metrics:#{#"height":#(HEIGHT_VIEW)}
                                                                        views:NSDictionaryOfVariableBindings(self.scrollView)]];
     
   
  
 
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_scrollView]-5-[pageControl(15)]"
                                                                      options:NSLayoutFormatAlignAllCenterX
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(self.scrollView, self.pageControl)]];
//[imgArr addObject:[UIImage imageNamed:...]];
    for (NSInteger index = 0; index<ViewCount; index++) {
        UIImageView *addSubview = [[UIImageView alloc] initWithFrame:CGRectMake(index*ViewWidth, 0, ViewWidth, ViewHeight)];
// addSubView.image = [imgArr objectAtIndex:index];
        [self.scrollView addSubview:addSubview];
    }
}
 
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    NSInteger currentOffset = scrollView.contentOffset.x;
    NSInteger index = currentOffset / ViewWidth;
     
    if (currentOffset % ViewWidth == 0) {
        pageControl.currentPage = index;
    }
}
 

Simple test app with scroll, content and image views - has problems with device rotation and zoom

In Xcode 5.1 I have created a very simple single view app for iPhone and put the source code at GitHub:
I have disabled Autolayout and put the following views in each other: scrollView -> contentView -> imageView (here fullscreen):
For the contentView and imageView I've disabled Autoresizing and set their frames to {0, 0, 1000, 1000} - both in the Storyboard and in the viewDidLoad method.
I have enabled double tap and pinch gestures for zooming.
For double tap the image is zoomed at 100% or 50% width.
This works initially, but after device rotation it breaks:
The zoom doesn't work properly and the image is offset - you can't scroll to its top left corner:
Here is my very short code in ViewController.m, please advice how to fix it:
- (void)viewDidLoad
{
[super viewDidLoad];
_imageView.frame = CGRectMake(0, 0, 1000, 1000);
_contentView.frame = CGRectMake(0, 0, 1000, 1000);
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
float scale = _scrollView.frame.size.width / 1000;
_scrollView.minimumZoomScale = scale;
_scrollView.maximumZoomScale = 2 * scale;
_scrollView.zoomScale = 2 * scale;
_scrollView.contentSize = CGSizeMake(1000, 1000);
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView*)scrollView
{
return _contentView;
}
- (IBAction)scrollViewDoubleTapped:(UITapGestureRecognizer*)sender
{
if (_scrollView.zoomScale < _scrollView.maximumZoomScale)
[_scrollView setZoomScale:_scrollView.maximumZoomScale animated:YES];
else
[_scrollView setZoomScale:_scrollView.minimumZoomScale animated:YES];
}
UPDATE: I've tried using Reveal app (here fullscreen), but couldn't find anything useful for me:
My source code seems to be okay, but in Interface Builder I had to disable "Autoresize Subviews" for the scrollView:

Programmatically scrolling UIScrollView then over-riding with manual scroll

I want a scroll view to automatically scroll when the user first loads the screen... so I'm using this code:
[UIView animateWithDuration:(float)1.25f
animations:^{
myScrollView.contentOffset = CGPointMake(2000, 800);
}
completion:nil];
So the code works great and the UIScrollView simulates a "scroll" (animation) to the CGPoint I want, but while it's scrolling if the user wants to manually put their finger on it and start scrolling or just stop it, the user can't over-ride this animation until after it's completed.
Anyone have any ideas of a better method to animate it with over-ride capabilities, and when to call said over-ride capabilities (I'm assuming this will involve touchesBegan on scrollView's view)
Simply add UIViewAnimationOptionAllowUserInteraction to your animation options:
[UIView animateWithDuration:1.25f delay:0.0f options:(UIViewAnimationOptionAllowUserInteraction) animations:^{
_scrollView.contentOffset = CGPointMake(0, 800);
}
completion:nil];
And cancel the animation on the UIScrollView scrollViewWillBeginDragging: delegate method
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//Storing current offset
CALayer *currentLayer = _scrollView.layer.presentationLayer;
CGPoint scrollBoundsOrigin = currentLayer.bounds.origin;
//Cancelling animations
[_scrollView.layer removeAllAnimations];
//Restore offset
_scrollView.contentOffset = scrollBoundsOrigin;
}

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!

Resources