I have an uipageviewcontroller and set a child viewcontroller in uipageviewcontroller named as contentviewcontroller.
thePageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation: UIPageViewControllerNavigationOrientationHorizontal options:nil];
thePageViewController.delegate = self;
thePageViewController.dataSource = self;
thePageViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
contentViewController = [[HBDocumentChildViewController alloc] initWithPDF:PDFDocument];
contentViewController.page = [modelArray objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:contentViewController];
[thePageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:thePageViewController];
[self.view addSubview:thePageViewController.view];
thePageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[thePageViewController didMoveToParentViewController:self];
self.view.gestureRecognizers = thePageViewController.gestureRecognizers;
self.view.backgroundColor = [UIColor underPageBackgroundColor];
I had done to load pdf files into pages in uipageviewcontroller with the help of a scroll view class(pdfScrollView).The followig is the content class of uipageviewcontroller and initializing the uiscrollview class.
#import "HBDocumentChildViewController.h"
#interface HBDocumentChildViewController ()<UIWebViewDelegate>
{
int currentPage;
NSString*localPath;
}
#end
#implementation HBDocumentChildViewController
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Create our PDFScrollView and add it to the view controller.
CGPDFPageRef PDFPage = CGPDFDocumentGetPage(thePDF, [_page intValue]);
pdfScrollView = [[PDFScrollView alloc] initWithFrame:self.view.frame];
[pdfScrollView setPDFPage:PDFPage];
[self.view addSubview:pdfScrollView];
self.view.backgroundColor = [UIColor whiteColor];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
pdfScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
The following is the uiscrollview class,
/*
File: PDFScrollView.m
Abstract: UIScrollView subclass that handles the user input to zoom the PDF page. This class handles swapping the TiledPDFViews when the zoom level changes.
Version: 2
*/
#import "PDFScrollView.h"
#import "TiledPDFView.h"
#import <QuartzCore/QuartzCore.h>
#interface PDFScrollView ()
// A low resolution image of the PDF page that is displayed until the TiledPDFView renders its content.
#property (nonatomic, weak) UIImageView *backgroundImageView;
// The TiledPDFView that is currently front most.
#property (nonatomic, weak) TiledPDFView *tiledPDFView;
// The old TiledPDFView that we draw on top of when the zooming stops.
#property (nonatomic, weak) TiledPDFView *oldTiledPDFView;
#end
#implementation PDFScrollView
{
CGPDFPageRef _PDFPage;
// Current PDF zoom scale.
CGFloat _PDFScale;
CGFloat _PDFScaleH;
}
#synthesize backgroundImageView=_backgroundImageView, tiledPDFView=_tiledPDFView, oldTiledPDFView=_oldTiledPDFView;
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.decelerationRate = UIScrollViewDecelerationRateFast;
self.delegate = self;
}
return self;
}
- (void)setPDFPage:(CGPDFPageRef)PDFPage;
{
CGPDFPageRetain(PDFPage);
CGPDFPageRelease(_PDFPage);
_PDFPage = PDFPage;
// Determine the size of the PDF page.
CGRect pageRect = CGPDFPageGetBoxRect(_PDFPage, kCGPDFMediaBox);
float actualHeight = pageRect.size.height;
float actualWidth = pageRect.size.width;
float imgRatio = actualWidth/actualHeight;
float maxRatio = 768.0/911.0;
if(imgRatio!=maxRatio){
if(imgRatio < maxRatio){
imgRatio = 911.0 / actualHeight;
actualWidth = imgRatio * actualWidth;
actualHeight = 911.0;
}
else{
imgRatio = 768.0 / actualWidth;
actualHeight = imgRatio * actualHeight;
actualWidth = 768.0;
}
}
pageRect.size = CGSizeMake(actualWidth, actualHeight);
UIGraphicsBeginImageContext(pageRect.size);
/*
Create a low resolution image representation of the PDF page to display before the TiledPDFView renders its content.
*/
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// First fill the background with white.
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,pageRect);
CGContextSaveGState(context);
// Flip the context so that the PDF page is rendered right side up.
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Scale the context so that the PDF page is rendered at the correct size for the zoom level.
CGContextScaleCTM(context, imgRatio,imgRatio);
CGContextDrawPDFPage(context, _PDFPage);
CGContextRestoreGState(context);
UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (self.backgroundImageView != nil) {
[self.backgroundImageView removeFromSuperview];
}
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
backgroundImageView.frame = pageRect;
backgroundImageView.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:backgroundImageView];
[self sendSubviewToBack:backgroundImageView];
self.backgroundImageView = backgroundImageView;
// Create the TiledPDFView based on the size of the PDF page and scale it to fit the view.
TiledPDFView *tiledPDFView = [[TiledPDFView alloc] initWithFrame:pageRect scale:imgRatio];
[tiledPDFView setPage:_PDFPage];
[self addSubview:tiledPDFView];
self.tiledPDFView = tiledPDFView;
}
- (void)dealloc
{
// Clean up.
CGPDFPageRelease(_PDFPage);
}
#pragma mark -
#pragma mark Override layoutSubviews to center content
// Use layoutSubviews to center the PDF page in the view.
- (void)layoutSubviews
{
[super layoutSubviews];
// Center the image as it becomes smaller than the size of the screen.
CGSize boundsSize = self.bounds.size;
CGRect frameToCenter = self.tiledPDFView.frame;
// Center horizontally.
if (frameToCenter.size.width < boundsSize.width)
frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
else
frameToCenter.origin.x = 0;
// Center vertically.
if (frameToCenter.size.height < boundsSize.height)
frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
else
frameToCenter.origin.y = 0;
self.tiledPDFView.frame = frameToCenter;
self.backgroundImageView.frame = frameToCenter;
/*
To handle the interaction between CATiledLayer and high resolution screens, set the tiling view's contentScaleFactor to 1.0.
If this step were omitted, the content scale factor would be 2.0 on high resolution screens, which would cause the CATiledLayer to ask for tiles of the wrong scale.
*/
self.tiledPDFView.contentScaleFactor = 1.0;
}
#pragma mark -
#pragma mark UIScrollView delegate methods
/*
A UIScrollView delegate callback, called when the user starts zooming.
Return the current TiledPDFView.
*/
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.tiledPDFView;
}
/*
A UIScrollView delegate callback, called when the user begins zooming.
When the user begins zooming, remove the old TiledPDFView and set the current TiledPDFView to be the old view so we can create a new TiledPDFView when the zooming ends.
*/
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
// Remove back tiled view.
[self.oldTiledPDFView removeFromSuperview];
// Set the current TiledPDFView to be the old view.
self.oldTiledPDFView = self.tiledPDFView;
[self addSubview:self.oldTiledPDFView];
}
/*
A UIScrollView delegate callback, called when the user stops zooming.
When the user stops zooming, create a new TiledPDFView based on the new zoom level and draw it on top of the old TiledPDFView.
*/
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
// Set the new scale factor for the TiledPDFView.
_PDFScale *= scale;
// Calculate the new frame for the new TiledPDFView.
CGRect pageRect = CGPDFPageGetBoxRect(_PDFPage, kCGPDFMediaBox);
pageRect.size = CGSizeMake(pageRect.size.width*_PDFScale, pageRect.size.height*_PDFScale);
// Create a new TiledPDFView based on new frame and scaling.
TiledPDFView *tiledPDFView = [[TiledPDFView alloc] initWithFrame:pageRect scale:_PDFScale];
[tiledPDFView setPage:_PDFPage];
// Add the new TiledPDFView to the PDFScrollView.
[self addSubview:tiledPDFView];
self.tiledPDFView = tiledPDFView;
}
#end
The page rendering is working correctly,but i didn't get the zoom in/out in the page displayed view.The uiscrollview delegate method not working in uipageviewcontroller.Please help me to find the solution for this problem.
Try This
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchDetected:)];
[thePageViewController addGestureRecognizer:pinchRecognizer];
pinchRecognizer.delegate=self;
The Method Is
-(void)pinchDetected:(UIPinchGestureRecognizer *)pinchRecognizer
{
CGFloat scale = pinchRecognizer.scale;
thePageViewController.transform = CGAffineTransformScale(thePageViewController.transform, scale, scale);
pinchRecognizer.scale = 1.0;
}
I have done the scroll and zoom in the uipageviewcontroller. I have changed the above UIPinchGestureRecognizer method for zoomIn and zoom out to the current page size.
-(void)pinchDetected:(UIPinchGestureRecognizer *)pinchRecognizer
{
if([pinchRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [pinchRecognizer scale];
}
if ([pinchRecognizer state] == UIGestureRecognizerStateBegan ||[pinchRecognizer state] == UIGestureRecognizerStateChanged)
{
CGFloat currentScale = [[[pinchRecognizer view].layer valueForKeyPath:#"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
const CGFloat maxScale = 1.5;
const CGFloat minScale = 1.0;
CGFloat newScale = 1 - (lastScale - [pinchRecognizer scale]);
newScale = MIN(newScale, maxScale / currentScale);
newScale = MAX(newScale, minScale / currentScale);
thePageViewController.view.transform = CGAffineTransformScale(thePageViewController.view.transform, newScale, newScale);
lastScale = [pinchRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}
Then,I have added the [pdfScrollView setScrollEnabled:YES];,[pdfScrollView setContentSize:CGSizeMake(768, 1100)];code in the viewDidLoad of the content viewcontroller(HBDocumentChildViewController)for scrolling the contentview in the uipageviewcontroller class.
- (void)viewDidLoad
{
[super viewDidLoad];
// Create our PDFScrollView and add it to the view controller.
CGPDFPageRef PDFPage = CGPDFDocumentGetPage(thePDF, [_page intValue]);
pdfScrollView = [[PDFScrollView alloc] initWithFrame:self.view.frame];
pdfScrollView.bouncesZoom = YES;
[pdfScrollView setScrollEnabled:YES];
[pdfScrollView setContentSize:CGSizeMake(768, 1100)];
[pdfScrollView setPDFPage:PDFPage];
[self.view addSubview:pdfScrollView];
self.view.backgroundColor = [UIColor whiteColor];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
pdfScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
pdfScrollView.delegate = pdfScrollView;
}
Try this code for zoomin and zoomout to the current page size and scrolling the content of the uipageviewcontroller.
Related
I have a UIViewController and it's view hierarchy looks like this:
UIView
UIScrollView
UIImageView
I have code that positions the image view in the middle of the scroll view's frame, like so:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self recenterContent:scrollView];
}
- (void)recenterContent:(UIScrollView *)scrollView {
//this centers the content when it is smaller than the scrollView's bounds
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}
This works fine when zooming the content, but when the view controller first loads it does not center. This is because the scrollView.contentSize is always 0. So my question is - when should I call this method after the scrollView.contentSize is set? When does that get set?
I have tried in viewDidLayoutSubviews, and the bounds of the scroll view is set then, but not the content size. Is there some method that I can use where the scroll view will be guaranteed to have the content size set?
Or is there a better way to keep the image centered when it is smaller than the scroll view? What I am trying to accomplish is to have it so the image view is not at the top of the scroll view and what I am using works, except when the scroll view's content size is not set. But if there is a better way of doing this without having to adjust the contentInset, I would be fine with that too.
Update
Here is what I have currently.
It is almost working, but no matter what I try, I cannot get it to look correct when the view loads. The way it works now is that it starts out off-center because when it calls the recenterContent method, before the view is displayed the content size of the scroll view is CGSizeZero, so the calculations are wrong. But if I try to recenter the content after the view has been displayed, then there is a visible delay before it gets centered.
I am just confused as to when the contentSize of the scroll view is set if I am using AutoLayout constraints to specify the size.
Here is my code. Can anyone see anything wrong with it?
#interface MyImageViewController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (strong, nonatomic) UIImageView *imageView;
#property (assign, nonatomic) BOOL needsZoomScale;
#end
#implementation MyImageViewController
- (void)loadView {
self.view = [[UIView alloc] init];
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.imageView];
self.needsZoomScale = YES;
[NSLayoutConstraint activateConstraints:#[
[self.scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.scrollView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[self.imageView.leadingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.leadingAnchor],
[self.imageView.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor],
[self.imageView.trailingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.trailingAnchor],
[self.imageView.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor]
]];
}
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapZoom:)];
doubleTapGesture.numberOfTapsRequired = 2;
[self.imageView addGestureRecognizer:doubleTapGesture];
}
- (CGRect)zoomRectForScrollView:(UIScrollView *)scrollView withScale:(CGFloat)scale withCenter:(CGPoint)center {
CGRect zoomRect;
//the zoom rect is in the content view's coordinates. At a zoom scale of 1.0, the zoom rect would be the size
//of the scroll view's bounds. As the zoom scale decreases, so more content is visible, the size of the rect
//grows.
zoomRect.size.width = scrollView.frame.size.width / scale;
zoomRect.size.height = scrollView.frame.size.height / scale;
//choose an origin so as to get the right center
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
return zoomRect;
}
- (void)doubleTapZoom:(UITapGestureRecognizer *)sender {
UIView *tappedView = sender.view;
CGPoint tappedPoint = [sender locationInView:tappedView];
if (tappedPoint.x <= 0) {
tappedPoint.x = 1;
}
if (tappedPoint.y <= 0) {
tappedPoint.y = 1;
}
if (tappedPoint.x >= tappedView.bounds.size.width) {
tappedPoint.x = tappedView.bounds.size.width - 1;
}
if (tappedPoint.y >= tappedView.bounds.size.height) {
tappedPoint.y = tappedView.bounds.size.height - 1;
}
CGFloat zoomScale;
if (self.scrollView.zoomScale < 1) {
zoomScale = 1;
} else if (self.scrollView.zoomScale < self.scrollView.maximumZoomScale) {
zoomScale = self.scrollView.maximumZoomScale;
} else {
zoomScale = self.scrollView.minimumZoomScale;
}
CGRect zoomRect = [self zoomRectForScrollView:self.scrollView withScale:zoomScale withCenter:tappedPoint];
[self.scrollView zoomToRect:zoomRect animated:YES];
}
- (UIScrollView *)scrollView {
if (!self->_scrollView) {
self->_scrollView = [[UIScrollView alloc] init];
self->_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self->_scrollView.minimumZoomScale = 0.1f;
self->_scrollView.maximumZoomScale = 4.0f;
self->_scrollView.bounces = YES;
self->_scrollView.bouncesZoom = YES;
self->_scrollView.delegate = self;
self->_scrollView.backgroundColor = [UIColor blackColor];
}
return self->_scrollView;
}
- (UIImageView *)imageView {
if (!self->_imageView) {
self->_imageView = [[UIImageView alloc] init];
self->_imageView.translatesAutoresizingMaskIntoConstraints = NO;
self->_imageView.userInteractionEnabled = YES;
}
return self->_imageView;
}
- (UIImage *)image {
return self.imageView.image;
}
- (void)setImage:(UIImage *)image {
self.imageView.image = image;
self.needsZoomScale = YES;
[self updateZoomScale];
}
- (void)updateZoomScale {
if (self.needsZoomScale && self.image) {
CGSize size = self.view.bounds.size;
if (size.width == 0.0f || size.height == 0.0f) {
return;
}
UIImage *image = self.image;
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
if (imageSize.width > 0 && imageSize.height > 0) {
CGFloat widthScale = size.width / imageSize.width;
CGFloat heightScale = size.height / imageSize.height;
CGFloat minScale = MIN(widthScale, heightScale);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.zoomScale = minScale;
self.needsZoomScale = NO;
}
}
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self updateZoomScale];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self recenterContent:self.scrollView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self recenterContent:self.scrollView];
}
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self recenterContent:scrollView];
}
- (void)recenterContent:(UIScrollView *)scrollView {
//this centers the content when it is smaller than the scrollView's bounds
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}
#end
The problem is that a UIImageView has an intrinsic content size of 0,0 -- so your code is initially putting the a 0x0 image view at the center of the scroll view.
I've made a few changes to the code you posted... see comments (I "wrapped" the changes in
// ---------------------------------
comment lines:
#interface MyImageViewController : UIViewController <UIScrollViewDelegate>
#end
#interface MyImageViewController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (strong, nonatomic) UIImageView *imageView;
#property (assign, nonatomic) BOOL needsZoomScale;
#end
#implementation MyImageViewController
- (void)loadView {
self.view = [[UIView alloc] init];
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.imageView];
self.needsZoomScale = YES;
// ---------------------------------
// respect safe area
UILayoutGuide *g = [self.view safeAreaLayoutGuide];
// saves on a little typing
UILayoutGuide *sg = [self.scrollView contentLayoutGuide];
// ---------------------------------
[NSLayoutConstraint activateConstraints:#[
[self.scrollView.leadingAnchor constraintEqualToAnchor:g.leadingAnchor],
[self.scrollView.topAnchor constraintEqualToAnchor:g.topAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:g.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:g.bottomAnchor],
[self.imageView.leadingAnchor constraintEqualToAnchor:sg.leadingAnchor],
[self.imageView.topAnchor constraintEqualToAnchor:sg.topAnchor],
[self.imageView.trailingAnchor constraintEqualToAnchor:sg.trailingAnchor],
[self.imageView.bottomAnchor constraintEqualToAnchor:sg.bottomAnchor]
]];
}
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapZoom:)];
doubleTapGesture.numberOfTapsRequired = 2;
[self.imageView addGestureRecognizer:doubleTapGesture];
}
- (CGRect)zoomRectForScrollView:(UIScrollView *)scrollView withScale:(CGFloat)scale withCenter:(CGPoint)center {
CGRect zoomRect;
//the zoom rect is in the content view's coordinates. At a zoom scale of 1.0, the zoom rect would be the size
//of the scroll view's bounds. As the zoom scale decreases, so more content is visible, the size of the rect
//grows.
zoomRect.size.width = scrollView.frame.size.width / scale;
zoomRect.size.height = scrollView.frame.size.height / scale;
//choose an origin so as to get the right center
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
return zoomRect;
}
- (void)doubleTapZoom:(UITapGestureRecognizer *)sender {
UIView *tappedView = sender.view;
CGPoint tappedPoint = [sender locationInView:tappedView];
if (tappedPoint.x <= 0) {
tappedPoint.x = 1;
}
if (tappedPoint.y <= 0) {
tappedPoint.y = 1;
}
if (tappedPoint.x >= tappedView.bounds.size.width) {
tappedPoint.x = tappedView.bounds.size.width - 1;
}
if (tappedPoint.y >= tappedView.bounds.size.height) {
tappedPoint.y = tappedView.bounds.size.height - 1;
}
CGFloat zoomScale;
if (self.scrollView.zoomScale < 1) {
zoomScale = 1;
} else if (self.scrollView.zoomScale < self.scrollView.maximumZoomScale) {
zoomScale = self.scrollView.maximumZoomScale;
} else {
zoomScale = self.scrollView.minimumZoomScale;
}
CGRect zoomRect = [self zoomRectForScrollView:self.scrollView withScale:zoomScale withCenter:tappedPoint];
[self.scrollView zoomToRect:zoomRect animated:YES];
}
- (UIScrollView *)scrollView {
if (!self->_scrollView) {
self->_scrollView = [[UIScrollView alloc] init];
self->_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self->_scrollView.minimumZoomScale = 0.1f;
self->_scrollView.maximumZoomScale = 4.0f;
self->_scrollView.bounces = YES;
self->_scrollView.bouncesZoom = YES;
self->_scrollView.delegate = self;
self->_scrollView.backgroundColor = [UIColor blackColor];
}
return self->_scrollView;
}
- (UIImageView *)imageView {
if (!self->_imageView) {
self->_imageView = [[UIImageView alloc] init];
self->_imageView.translatesAutoresizingMaskIntoConstraints = NO;
self->_imageView.userInteractionEnabled = YES;
}
return self->_imageView;
}
- (UIImage *)image {
return self.imageView.image;
}
- (void)setImage:(UIImage *)image {
self.imageView.image = image;
// ---------------------------------
// set the frame here
self.imageView.frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
// ---------------------------------
// not needed ... unless maybe changing the image while view is showing?
//self.needsZoomScale = YES;
//[self updateZoomScale];
}
- (void)updateZoomScale {
if (self.needsZoomScale && self.image) {
CGSize size = self.view.bounds.size;
if (size.width == 0.0f || size.height == 0.0f) {
return;
}
UIImage *image = self.image;
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
if (imageSize.width > 0 && imageSize.height > 0) {
CGFloat widthScale = size.width / imageSize.width;
CGFloat heightScale = size.height / imageSize.height;
CGFloat minScale = MIN(widthScale, heightScale);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.zoomScale = minScale;
self.needsZoomScale = NO;
}
}
}
// ---------------------------------
// Don't need this
//- (void)viewWillLayoutSubviews {
// [super viewWillLayoutSubviews];
// [self updateZoomScale];
//}
// ---------------------------------
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// ---------------------------------
// update zoom scale here
[self updateZoomScale];
// ---------------------------------
[self recenterContent:self.scrollView];
}
// ---------------------------------
// Don't need this
//- (void)viewDidAppear:(BOOL)animated {
// [super viewDidAppear:animated];
// [self recenterContent:self.scrollView];
//}
// ---------------------------------
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self recenterContent:scrollView];
}
- (void)recenterContent:(UIScrollView *)scrollView {
//this centers the content when it is smaller than the scrollView's bounds
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}
#end
and here's how I call it:
MyImageViewController *vc = [MyImageViewController new];
UIImage *img = [UIImage imageNamed:#"bkg"];
if (nil == img) {
NSLog(#"Could not load image!!!!");
return;
}
[vc setImage:img];
[self.navigationController pushViewController:vc animated:YES];
I have the following problem, guys. I have an app that is pretty much like Apple's PhotoScroller. I want to jump from image to image by swiping the screen. I can do that, but I can't zoom the images. Here's the problem - I have an array with images. If the array has only one object inside, there's no problem with zooming. But if there are more images in the array, they acts weirdly when I try to zoom. The image is not being zoomed and it goes where it wants off the screen. Here is my code:
int pageWidth = 1024;
int pageHeight = 768;
int counter = 0;
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, pageWidth, pageHeight)];
CGRect containerFrame = self.view.frame;
scrollView = [[UIScrollView alloc] initWithFrame:containerFrame];
[self.view addSubview:scrollView];
NSMutableArray *all = [[NSMutableArray alloc] init];
[all addObject:[UIImage imageNamed:#"222.jpg"]];
CGSize scrollSize = CGSizeMake(pageWidth * [all count], pageHeight);
[scrollView setContentSize:scrollSize];
for (UIImage *image in all)
{
UIImage *pageImage = [[UIImage alloc] init];
pageImage = [all objectAtIndex:counter];
CGRect scrollFrame = CGRectMake(pageWidth * counter, 0, pageWidth, pageHeight);
miniScrollView = [[UIScrollView alloc] initWithFrame:scrollFrame];
[scrollView addSubview:miniScrollView];
CGSize miniScrollSize = CGSizeMake(pageImage.size.width, pageImage.size.height);
[miniScrollView setContentSize:miniScrollSize];
UIImageView *tempImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, pageImage.size.width, pageImage.size.height)];
tempImageView.image = [all objectAtIndex:counter];
self.imageView = tempImageView;
miniScrollView.maximumZoomScale = 3.0;
miniScrollView.minimumZoomScale = 1.0;
miniScrollView.clipsToBounds = YES;
miniScrollView.delegate = self;
miniScrollView.showsHorizontalScrollIndicator = NO;
miniScrollView.showsVerticalScrollIndicator = NO;
miniScrollView.bouncesZoom = YES;
[miniScrollView addSubview:imageView];
counter ++;
}
[scrollView setPagingEnabled:YES];
[scrollView setScrollEnabled:YES];
}
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return imageView;
}
Do you have any ideas what's wrong? Because I am trying to get this work almost 2 weeks.
I also worked on such sort of App. The first thing that you can do is to take a separate subclass of your ScrollView so that all the paging and zooming operations can be handled easily. In your scrollView Class, You can take reference from the following code snippet.
#interface PhotoScrollView : UIScrollView<UIScrollViewDelegate,UIGestureRecognizerDelegate>
{
int finalPhotoWidth;
int finalPhotoHeight;
}
// to contain image
#property (strong, nonatomic) UIImageView *imageView;
- (id)initWithFrame:(CGRect)frame andImage:(UIImage *)photo
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
[self initializeScrollViewWithImage:photo];
//setting gesture recognizer for single tap
UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewSingleTapped:)];
singleTapRecognizer.delegate = self;
singleTapRecognizer.numberOfTapsRequired = 1;
[self addGestureRecognizer:singleTapRecognizer];
//setting gesture recognizer for Double tap
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.delegate = self;
doubleTapRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:doubleTapRecognizer];
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
singleTapActivated = FALSE;
self.backgroundColor = [UIColor blackColor];
self.minimumZoomScale = 1.0f;
self.maximumZoomScale = 15.0f;
self.zoomScale = 1.0f;
self.delegate = self;
}
return self;
}
//for sizing the frame by giving height and width
-(void)initializeScrollViewWithImage:(UIImage*)photo
{
finalPhotoWidth = photo.size.width;
finalPhotoHeight = photo.size.height;
//Pre-checking of frame and setting the height and width accordingly
if (finalPhotoHeight > self.frame.size.height)
{
// if photo height is bigger than frame height, re-adjust the photo height and width accordingly
finalPhotoHeight = self.frame.size.height;
finalPhotoWidth = (photo.size.width/photo.size.height) * finalPhotoHeight;
}
if (finalPhotoWidth > self.frame.size.width)
{
// if photo width is bigger than frame width, re-adjust the photo height and width accordingly
finalPhotoWidth = self.frame.size.width;
finalPhotoHeight = (photo.size.height/photo.size.width) * finalPhotoWidth;
}
if (finalPhotoHeight < self.frame.size.height && finalPhotoWidth < self.frame.size.width)
{
// if the photo is smaller than frame, increase the photo width and height accordingly
finalPhotoWidth = self.frame.size.width;
finalPhotoHeight = self.frame.size.height;
}
//initialising imageView with the thumbnail photo
self.imageView = [[UIImageView alloc] initWithImage:photo];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
//setting frame according to the modified width and height
if(!isnan(finalPhotoWidth) && !isnan(finalPhotoHeight))
{
self.imageView.frame = CGRectMake( (self.frame.size.width - finalPhotoWidth) / 2, (self.frame.size.height - finalPhotoHeight)/2, finalPhotoWidth, finalPhotoHeight);
}
// setting scrollView properties
self.contentSize = self.imageView.frame.size;
// add image view to scroll view
[self addSubview:self.imageView];
}
//to deny the simultaneous working of single and double taps
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return NO;
}
// Gesture handleer for single tap gesture
-(void)scrollViewSingleTapped:(UITapGestureRecognizer *)recognizer
{
if(!singleTapActivated)
{
//do as per requirement
singleTapActivated = TRUE;
}
else
{
//do as per requirement
singleTapActivated = FALSE;
}
}
//for zooming after double tapping
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer
{
//to check whether image is zoomed
if (self.zoomScale != 1.0f)
{
//if image is zoomed, then zoom out
[self setZoomScale:1.0 animated:YES];
self.imageView.frame = CGRectMake( (self.frame.size.width - finalPhotoWidth) / 2, (self.frame.size.height - finalPhotoHeight)/2, finalPhotoWidth, finalPhotoHeight);
[self.observer photoZoomStopped];
}
else
{
// get the point of image which is double tapped
CGPoint pointInView = [recognizer locationInView:self.imageView];
// Get a zoom scale that's zoomed in slightly, capped at the maximum zoom scale specified by the scroll view
CGFloat newZoomScale = self.zoomScale * 4.0f;
newZoomScale = MIN(newZoomScale, self.maximumZoomScale);
// Figure out the rect we want to zoom to, then zoom to it
CGSize scrollViewSize = self.imageView.frame.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self zoomToRect:rectToZoomTo animated:YES];
}
}
// To re-center the image after zooming in and zooming out
- (void)centerScrollViewContents
{
CGSize boundsSize = self.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width)
{
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
}
else
{
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height)
{
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
}
else
{
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
//for zooming in and zooming out
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
if (self.zoomScale > 1.0f)
{
[self.observer photoZoomStarted];
[self centerScrollViewContents];
}
else
{
self.bouncesZoom = NO;
[self.observer photoZoomStopped];
// for zooming out by pinching
[self centerScrollViewContents];
}
// The scroll view has zoomed, so we need to re-center the contents
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
Please let me know if it helps. Thanks :)
This question may seem simple but please hear me out. I've made a huge mistake my project contains 300+ activities that are in a single storyboard and I've just recently figured out that Xcode is terrible at handling that. So what I would like to do is to have my different content which is now currently displayed in separate activities written into the code so that I only have to have the minimum amount of activities in my storyboard. This may be hard to follow I apologize.
In my head here is how it should work. I'l give an example.
In my app I have a section called MAPS, from the homepage you click on the maps button and it brings you to a menu which has buttons with the maps names on them. As you would expect the name of the map on the button brings you the the activity with said map. The problem is that each map is in a separate activity that is linked too inside the storyboard.
What I would like to do is programatically call each separate activity from within one activity in my storyboard effectively replacing 20+ activities with 1.
Here is what my map view controllers look like (they're actually .png images with a scrollview)
MapRoute1.h
#import "ViewController.h"
#interface MapRoute1 : ViewController <UIScrollViewDelegate>
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) UIImageView *imageView;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
#end
MapRoute1.m
#import "MapRoute1.h"
#interface MapRoute1 ()
#end
#implementation MapRoute1
#synthesize imageView = _imageView;
#synthesize scrollView = _scrollView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
// 1
CGPoint pointInView = [recognizer locationInView:self.imageView];
// 2
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
// 3
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
// 4
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that you want to zoom
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so you need to re-center the contents
[self centerScrollViewContents];
}
- (void)viewDidLoad {
[super viewDidLoad];
// 1
UIImage *image = [UIImage imageNamed:#"map_route1.png"];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
// 2
self.scrollView.contentSize = image.size;
// 3
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 4
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
// 5
self.scrollView.maximumZoomScale = 1.5f;
self.scrollView.zoomScale = minScale;
// 6
[self centerScrollViewContents];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
So as you can tell the only part that changes in each separate map is this line of code:
UIImage *image = [UIImage imageNamed:#"map_route1.png"];
What I would like to do is have some sort of list of all my images and when the button is clicked for that map(image) it will replace this line of code with the image name or Map that the button should link too. The overall code and everything should only be one activity in my storyboard that is linked to be each button in the MAPS menu.
Currently all activities are linked by a simple "control click-drag" in the storyboard and are contained inside of a navigation controller which provides back navigation.
I would like to keep the back navigation with the navigation controller but simply replace the image inside of the scrollview.
Thanks in advance!
-Derek
Can anybody help me with the sample code for embedding a zoomable scroll view within a paging scroll view so that each page can be zoomed and panned individually?
Also the navigation structure is like tab bar controller --> navigation controller with buttons (on push of the buttons) --> view controller where in the horizontal scrollview of images has to be implemented with pagination and zooming.
Here is the link to the tutorial that I followed: How To Use UIScrollView to Scroll and Zoom Content
The code I've implemented is following:
#import "ViewCorpBrochureController.h"
#interface ViewCorpBrochureController ()
#property (nonatomic, strong) NSArray *pageImages;
#property (nonatomic, strong) NSMutableArray *pageViews;
#property (nonatomic, strong) UIImageView *imageView;
- (void)loadVisiblePages;
- (void)loadPage:(NSInteger)page;
- (void)purgePage:(NSInteger)page;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
#end
#implementation ViewCorpBrochureController
#synthesize scrollView = _scrollView;
#synthesize pageControl = _pageControl;
#synthesize pageImages = _pageImages;
#synthesize pageViews = _pageViews;
#synthesize imageView = _imageview;
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
// Get the location within the image view where we tapped
CGPoint pointInView = [recognizer locationInView:self.imageView];
// Get a zoom scale that's zoomed in slightly, capped at the maximum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
// Figure out the rect we want to zoom to, then zoom to it
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}
#pragma mark -
- (void)loadVisiblePages {
// First, determine which page is currently visible
CGFloat pageWidth = self.scrollView.frame.size.width;
NSInteger page = (NSInteger)floor((self.scrollView.contentOffset.x * 2.0f + pageWidth) / (pageWidth * 2.0f));
// Update the page control
self.pageControl.currentPage = page;
// Work out which pages we want to load
NSInteger firstPage = page - 1;
NSInteger lastPage = page + 1;
// Purge anything before the first page
for (NSInteger i=0; i<firstPage; i++) {
[self purgePage:i];
}
for (NSInteger i=firstPage; i<=lastPage; i++) {
[self loadPage:i];
}
for (NSInteger i=lastPage+1; i<self.pageImages.count; i++) {
[self purgePage:i];
}
}
- (void)loadPage:(NSInteger)page {
if (page < 0 || page >= self.pageImages.count) {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView == [NSNull null]) {
CGRect frame = self.scrollView.bounds;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0.0f;
UIImageView *newPageView = [[UIImageView alloc] initWithImage:[self.pageImages objectAtIndex:page]];
newPageView.contentMode = UIViewContentModeScaleAspectFit;
newPageView.frame = frame;
[self.scrollView addSubview:newPageView];
[self.pageViews replaceObjectAtIndex:page withObject:newPageView];
}
}
- (void)purgePage:(NSInteger)page {
if (page < 0 || page >= self.pageImages.count) {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Remove a page from the scroll view and reset the container array
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView != [NSNull null]) {
[pageView removeFromSuperview];
[self.pageViews replaceObjectAtIndex:page withObject:[NSNull null]];
}
}
#pragma mark -
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"CorporateBrochure";
self.pageImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"CB1.jpg"],
[UIImage imageNamed:#"CB2.jpg"],
[UIImage imageNamed:#"CB3.jpg"],
[UIImage imageNamed:#"CB4.jpg"],
[UIImage imageNamed:#"CB5.jpg"],
[UIImage imageNamed:#"CB6.jpg"],
nil];
NSInteger pageCount = self.pageImages.count;
// Set up the page control
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
// Set up the array to hold the views for each page
self.pageViews = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < pageCount; ++i) {
[self.pageViews addObject:[NSNull null]];
}
/* If I use this the subview appears below the image
UIImage *image = self.imageView.image;
//UIImage *image = [UIImage imageNamed:#"CB2.jpg"];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = image.size;
*/
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGSize pagesScrollViewSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * self.pageImages.count, pagesScrollViewSize.height);
[self loadVisiblePages];
}
- (void)viewDidUnload {
[super viewDidUnload];
self.scrollView = nil;
self.pageControl = nil;
self.pageImages = nil;
self.pageViews = nil;
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that we want to zoom
return self.imageView;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationMaskAll);
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Load the pages which are now on screen
[self loadVisiblePages];
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so you need to re-center the contents
[self centerScrollViewContents];
}
#end
Is there a specific issue you are having with Ray Wenderlich's tutorial?
Apple's documentation has a very good example of how to implement a paging scroll view, see the Photo Scroller sample code here. I would suggest starting there.
From the above link:
"PhotoScroller" demonstrates the use of embedded UIScrollViews and
CATiledLayer to create a rich user experience for displaying and
paginating photos that can be individually panned and zoomed.
Note: If your images are not overly large, you can get away with not using CATiledLayer.
EDIT 1: See my answer to this question about how to modify Apple's Photo Scroller code to make the UIPageViewController a subview of your own view controller (and not the rootViewController).
EDIT 2: See this sample project on github.
Hey I'm following this tutorial and currently on the first step but am experiencing some weird behavior. The tutorial is on creating scroll views for images and ends with showing how to create a paged horizontal ScrollView with a page controller. I followed the first section and things seemed to be working fine. However when I ran the code it did not perform properly, after about an hour of debugging I downloaded the final project, ran it on my phone to check if it was working correctly (it was) then copied the code into the required files in my project. My project still wouldn't run. So I figured it must be the way in which my story board was configured on the project setup. I then oped the working example cleared their storyboard and created the views and segues my self, in the exact same way and order I had done in my project. Bam! it still worked, when I deleted all views in my story board and recreated them in the exact same fashion, still nothing. Could someone look into this tutorial and tell me if I'm going crazy or they experience similar results. I have narrowed it down to two possibilities, the project provided at the end of the tutorial was created differently and the delegates or outlets were hooked up in a way that only works with the project, or the project was created with an earlier version of the iPhone development kit that works with the current version of XCode but not when I create a new project. Then again I am fairly new to this so I will throw up the code I'm using and hope for the best.
ViewController.h file:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIScrollViewDelegate>
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#end
ViewController.m file:
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) UIImageView *imageView;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
#end
#implementation ViewController
#synthesize scrollView = _scrollView;
#synthesize imageView = _imageView;
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
// Get the location within the image view where we tapped
CGPoint pointInView = [recognizer locationInView:self.imageView];
// Get a zoom scale that's zoomed in slightly, capped at the maximum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
// Figure out the rect we want to zoom to, then zoom to it
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Set a nice title
self.title = #"Image";
// Set up the image we want to scroll & zoom and add it to the scroll view
UIImage *image = [UIImage imageNamed:#"photo1.png"];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
// Tell the scroll view the size of the contents
self.scrollView.contentSize = image.size;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Set up the minimum & maximum zoom scales
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.maximumZoomScale = 1.0f;
self.scrollView.zoomScale = minScale;
[self centerScrollViewContents];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
// Return the view that we want to zoom
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
// The scroll view has zoomed, so we need to re-center the contents
[self centerScrollViewContents];
}
#end
The above code doesn't work as it does in the tutorials project. The following UIScrollView delegate method viewForZoomingInScrollView produces this error:
2013-05-14 10:01:18.585 TestScrollView[3809:907] Made it
May 14 10:01:18 Scott-Laroses-iPhone TestScrollView[3809] : CGAffineTransformInvert: singular matrix.
May 14 10:01:18 Scott-Laroses-iPhone TestScrollView[3809] : CGAffineTransformInvert: singular matrix.
which makes me think the delate isn't properly set up despite me doing it exactly the same as in the tutorial and programmatically when that didn't work. Any ideas?
Your problem may be from the zoomScale and minimumZoomScale if the value assigned is 0. Make sure that the value is never 0 for those two properties.
I figured it out! The issues was that the auto layout constraints were messing up the off screen views. So I disabled auto layout and it worked fine.