Zoom and Pan on a UIPageViewController - ios

I have a simple application which consists of a UITableViewController with cells of Languages. When the user clicks a specific language, it brings up a UIPageViewController to show horizontally, 6 images in a pageView. That part is working really well and the images are loading.
I have implemented this, using this tutorial: http://code.tutsplus.com/tutorials/using-scrollstyle-with-uipageviewcontroller--mobile-13551.
I would like to achieve a zoom and pan functionality within this as well now.
I have zoom working with the following code.
CGFloat scale = pinchRecognizer.scale;
self.pageViewController.view.transform = CGAffineTransformScale(self.pageViewController.view.transform, scale, scale);
pinchRecognizer.scale = 1.0;
The problem is that the zoom is always in the centre of the image with no way to pan around the image like you can in a normal Photo on an iOS device.
I have found lots of good tutorials on how to achieve this, like http://iosdeveloperzone.com/2012/07/07/tutorial-all-about-images-part-2-panning-zooming-with-uiscrollview/ as an example. However, my confusion stems from the very first part.
I am loading my UIPageViewController using a NSArray:
self.modelArray = [NSMutableArray arrayWithObjects:[[ImageModel alloc] initWithImageName:#"3facts-english-page1.jpg"], [[ImageModel alloc] initWithImageName:#"3facts-english-page2.jpg"], [[ImageModel alloc] initWithImageName:#"3facts-english-page3.jpg"], nil];
How to I extract each image into a UIImageView?
I can see that fundamentally, I need to have most of this code to enable the Pan:
UIImage* image = [UIImage imageNamed:#"KinkakuJi"];
self.imageView.image = image;
[self.imageView sizeToFit];
self.scrollView.contentSize = image.size;
In my case however, I'm not loading a single UIImage but rather a NSArray. How do I extract the UIImage from the NSArray to make this pan happen?
Any thoughts on this would really be appreciated.

Related

Display images in ScrollView

I want to display some images in a scroll view, but I am facing some issues.
Here is what I have:
- (void)viewDidLoad
{
[super viewDidLoad];
myObject = [[NSMutableArray alloc] init];
[myObject addObject:#"tut_5.jpg"];
[myObject addObject:#"tut_2.jpg"];
[myObject addObject:#"tut_3.jpg"];
[myObject addObject:#"tut_4.jpg"];
pageControlBeingUsed = NO;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHight = screenRect.size.height;
for (int i = 0; i < [myObject count]; i++) {
CGRect frame;
frame.origin.x = self.takeTourScrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.takeTourScrollView.frame.size;
NSString *val = [myObject objectAtIndex:i];
UIImage* theImage = [UIImage imageNamed:val];
UIImageView *img=[[UIImageView alloc] initWithFrame:CGRectMake(screenWidth*i,0,185 ,284)];
img.image = theImage;
[self.takeTourScrollView addSubview:img];
}
The first first image seems to be ok.
Then when I swipe left I am getting a blank screen
I swipe left again and then I see my second picture. And it goes like that for all the 4 images.
Any ideas what am I doing wrong ?
There are system components that will give you what you want with much less work and a more refined user experience. Using a UICollectionView is one possibility, as mentioned by Vive in her answer. My suggestion would be a UIPageViewController. There is a sample app called PhotoScroller from Apple that shows how to implement a UI very much like what you describe. Do a search in the Xcode docs for "PhotoScroller" to find it.
I'd advise to use UITableView/UICollectionView for such behaviour. You can set the image in each cell and they'll be reused - it will be much better in terms of memory management.
The tableView will by the way place all elements in proper places (you just need to define the height of the cell or the layout).
It will also properly resize on events (eg phone rotation).
Also, you should try to avoid hardcoding the sizes:
CGRectMake(screenWidth*i,0,185 ,284)
You will fail in case of smaller / bigger devices. It'll be a great pain to maintain the code after some time.
-- edit --
After #Duncan C posted his answer, I've noticed you're looking for a paging system. You can go with building your own solution either on UIPageViewController or on UICollectionView. However you can also use third party libraries, I really enjoy this one: https://www.cocoacontrols.com/controls/bwwalkthrough. It has a support of different animations and does a ton of stuff for you :).
you already set the x position of uiscrollview. do not need to set the x postion of UIImageView.Because you used imageview as a subview of uiimagescrollview.
change this line to
UIImageView *img=[[UIImageView alloc] initWithFrame:CGRectMake(screenWidth*i,0,185 ,284)];
with
UIImageView *img=[[UIImageView alloc] initWithFrame:CGRectMake(0,0,185 ,284)];

iOS Image Viewer: Set Zoomed Image Back when Scrolling

I have a gallery view that I use to, surprise, view images. :) I wanted it to be very similar to the standard iOS photo app with the same basic functionality: scrolling through images that are zoomable. To do this, I use a regular scroll view which I then populate with other scroll views that contain the images. The basic idea is something like this:
And, I do the populating the following way:
for (int i = 0; i < [imageGalleryArray count]; i++)
{
UIScrollView *imageContainer = [[UIScrollView alloc] initWithFrame:CGRectMake((windowSize.width * i), 0.0f, windowSize.width, windowSize.height)];
imageContainer.contentSize = CGSizeMake(windowSize.width, windowSize.height);
imageContainer.showsHorizontalScrollIndicator = NO;
imageContainer.showsVerticalScrollIndicator = NO;
imageContainer.bouncesZoom = YES;
imageContainer.delegate = self;
imageContainer.minimumZoomScale = 1.0f;
imageContainer.maximumZoomScale = 3.0f;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, windowSize.width, windowSize.height)];
imageView.image = [[imageGalleryArray objectAtIndex:i] objectForKey:#"image"];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.tag = IMAGEZOOMVIEW;
imageView.opaque = YES;
[imageContainer addSubview:imageView];
[galleryScrollView addSubview:imageContainer];
// Add it to array for proper removal later
[contentScrollViewSubviewList addObject:imageContainer];
}
Quite simple and it works fine. The only problem arises when I am zoomed into an image and want to scroll to another. The expected behavior in this case would be to set the zoom level of all images back to their original size. I do this in the scrollViewDidEndDecelerating delegate method like so:
if (scrollView.tag == GALLERYSCROLLVIEW)
{
for (int i = 0; i < [contentScrollViewSubviewList count]; i++)
{
[[contentScrollViewSubviewList objectAtIndex:i] setZoomScale:1.0f animated:YES];
}
}
Unfortunately, it does not work (or rather not as intended). I did some poking around and found out that the image does indeed animate back to its original size when it is the active slide. To elaborate: if I zoom into an image and scroll to another, the image stays zoomed in. When I scroll back to it, that is when it zooms back to its original size. I, however, need the image to scale back as soon as I move away from it ().
just like in the iOS image viewer).
Frankly, I do not understand the whole zoom mechanic enough to find out how to do this properly, and the UIScrollView documentation is of no help either. Could someone show me how to solve my problem?

how to implement this animation in iOS?

I am not sure about how to present this question because I don't know the animation term that I should use.
I need to know about this tree presentation animation. As it is appearing form root to top.
Please take a look on attached .gif file and let me know if anyone know about this animation or if you can guide me with example.
I will really appreciate.
Thanks for your time.
The best option for you is to split up the gif into multiple images and then do the following:
NSArray *gifImagesArray = [NSArray arrayWithObjects:imageOne, imageTwo, imageThree, nil];
imageView.animationImages = animateImagesArray;
imageView.animationRepeatCount = 1;
imageView.animationDuration = 1.0f;
[imageView startAnimating];
Edit following comments:
If you can't use multiple images you have to write the animation yourself.
One suggestion is to add a circular mask to the UIImage and animate it's removal.
This link explains how to draw a circular CALayer: Circular Progress Bars in IOS
This one here will show you how to create a mask on a UIImage: Simply mask a UIView with a rectangle
now all you have to do is a simple animation.
I have a solution that will work for this problem.
We can use gif image and convert it into UIImage object. So image object will work same as animation.
Thanks all for your answers.
In my opinion, I will separate this animation to 4 parts:
animation to show full black tree from center.
animation to show Texts
animation to show blue leafs.
animation to show 2 buttons at bottom.
You can use this framework https://github.com/facebook/pop to operate all animations step by step or even Core Animation.
Please do more research... I think you will success to make one.
You can achieve this animation using simple UIImageView, that can load multiple images using animationImages property of UIImageView.
First, create one NSMutablerArray of images.
then asssign that images to imageView, and animationDuration for that images.
UIImageView *animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 95, 86, 193)];
animationImageView.animationImages = images;
animationImageView.animationDuration = 0.5;
add the imageview to view.
[self.view addSubview:animationImageView];
[animationImageView startAnimating];

iOS: understanding frame and views

I am working programmatically an application for iOS based on a ViewController. I am trying to do so programmatically as I want to understand the underlying concepts.
I have created a subclass of UIImageView and initialized this using an image. In the initialization method I added also a second UIImageView as I would like to handle the two differently but be part of the same object. Ultimately I would like to be able to scale the object (and hence the 2 UIImages) according to the device screen resolution (e.g. if resolution is low then I will scale the two images by 50%). I want to do this because I would like to be able to implement a zoom in and zoom out feature as well as supporting multiple resolutions and screen layouts.
Additional information:
The two images have different size (500x500 pixels) and (350x350
pixels).
My questions are:
how do I position the second image exactly in the center of the first? (I used the center property of the main UIImage but I think I got it wrong.. I thought that the center was the exact center of the square but either I am using it incorrectly or there is something I am missing)
are there any negative side effects for using this approach (UIView subclass class containing an additional UIView?) (E.g. Is it going to create confusion when applying transformation algorithms? Does it reduce the randering speed? Or more simply is it a bad design pattern?)
I find it difficult to understand the positioning of the second image. See code snipped below, this is what I use:
CGRect innerButtonFrame = CGRectMake(self.center.x/2, self.center.y/2,innerButtonSelectedImage.size.width,innerButtonSelectedImage.size.height);
Taken from:
-(id) initWithImage:(UIImage *)image
{
if(self = [super initWithImage:image]){
//
self.userInteractionEnabled = true;
// Initialize gesture recognizers
UITapGestureRecognizer *tapInView = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapInImageView:)];
[self addGestureRecognizer:tapInView];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressInView:)];
[self addGestureRecognizer:longPress];
// Initialize labels
..
// Inner circle image
innerButtonView = [[UIImageView alloc] init];
innerButtonSelectedImage = [UIImage imageNamed:#"inner circle.png"];
CGRect innerButtonFrame = CGRectMake(self.center.x/2, self.center.y/2,innerButtonSelectedImage.size.width,innerButtonSelectedImage.size.height);
innerButtonView.frame = innerButtonFrame;
[innerButtonView setImage:innerButtonSelectedImage];
// Add additional ui components to view
[self addSubview:innerButtonView];
..
[self addSubview:descriptionLabel];
}
return self;
}
EDIT: This is how it looks like if I change the positioning code to the following:
CGRect innerButtonFrame = CGRectMake(0, 0,innerButtonSelectedImage.size.width,innerButtonSelectedImage.size.height);
innerButtonView.frame = innerButtonFrame;
I also don't understand why the image is bigger than the screen.. as the blue one should be 500x500 pixel wide and the screen of the iPhone 6 should be 1334 x 750.
How about:
CGRect innerButtonFrame = CGRectMake(0, 0, innerButtonSelectedImage.size.width,innerButtonSelectedImage.size.height);
innerButtonFrame.center = self.center;
If you need 500*500 circle then add the circle half means Replace 500*500 with 250*250 . And small circle replace 350*350 with 175*175 And solve your problem.
I hope your problem will solve..Enjoy
Thanks..

Using GPUImage With a UIView

I'm trying to integrate GPUImage into my app. Specifically, I want to apply the Sphere Refraction filter on my main view. Thing is, GPUImage works with UIImage, not with UIView. In order to create a UIImage representation of my view hierarchy, I'm using [CALayer renderInContext], which takes a long time to complete. The net result is that my animations look clunky.
Here's the code that's called in my CADisplayLink handler:
- (void)onDisplayLink:(CADisplayLink*)theDisplayLink {
self.mainView.layer.opaque = YES;
UIGraphicsBeginImageContextWithOptions(self.sphereView.bounds.size, self.sphereView.opaque, [[UIScreen mainScreen] scale]);
[self.sphereView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* mainViewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.sourcePicture = [[GPUImagePicture alloc] initWithImage:mainViewImage smoothlyScaleOutput:NO];
self.sphereRefractionFilter = [[GPUImageSphereRefractionFilter alloc] init];
self.sphereRefractionFilter.radius = 0.5;
self.sphereRefractionFilter.refractiveIndex = 0.25;
[self.sphereRefractionFilter setInputRotation:kGPUImageRotate180 atIndex:0];
[self.sphereRefractionFilter addTarget:self.mainView];
[self.sourcePicture addTarget:self.sphereRefractionFilter];
[self.sourcePicture processImage];
}
The view I'm trying to render using this code has a background image, and about 5-50 smaller images laid out on it, whose positions are modified in real-time. Imagine a sphere with multiple moving markers on it in various places.
Using this code, I'm able to render about 10 FPS. Question is: is there any way to do this faster?
Anyone?

Resources