Adding subviews to a UIView at initialization - ios

In the initialization method for a UIView subclass I am adding several UIImageViews. When the view is rendered they are present in the subviews array and have valid frame rects, but are not drawn on the screen. Does anyone know why this happens?
- (id) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self)
return nil;
[self commonInit];
return self;
}
- (void) commonInit {
_padding = 128.0f;
// Add the chord views here
_chordImages = [NSArray arrayWithObjects:[UIImage imageNamed:#"fn_maj_x_x_x_x_1_e_1_fn"], [UIImage imageNamed:#"en_maj_x_x_x_x_1_e_1_en"], [UIImage imageNamed:#"dn_maj_x_x_x_x_1_d_1_f#"], [UIImage imageNamed:#"an_min_x_x_x_x_1_a_1_en"], nil];
CGFloat containerWidth = [self bounds].size.width;
NSLog(#"containerWidth: %f",containerWidth);
CGFloat chordWidth = [[_chordImages objectAtIndex:0] size].width;
NSLog(#"chordWidth: %f",chordWidth);
CGFloat chordMarkerWidth = [[[GVChordMarkerView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)] frame].size.width;
NSLog(#"chordMarkerWidth: %f",chordMarkerWidth);
// [self setFrame:CGRectMake(chordXPosition, 0, [self bounds].size.width, [self bounds].size.height]);
CGFloat chordXPosition = 0;
CGFloat chordXSpacing = (containerWidth/4 - (chordMarkerWidth + chordWidth));
chordXPosition += containerWidth/4;
for (NSUInteger i = 0; i < [_chordImages count]; i++) {
chordXPosition += chordXSpacing;
CGRect chordImageViewFrame = CGRectMake(chordXPosition, 0, [[_chordImages objectAtIndex:0] size].width, [[_chordImages objectAtIndex:0] size].height);
UIImageView *chordImageView = [[UIImageView alloc] initWithFrame:chordImageViewFrame];
[self addSubview:chordImageView];
chordXPosition += (chordMarkerWidth + chordWidth);
}
NSLog(#"Finished");
}

Could it be that your UIImageview is just empty? Where did you add the image?

Related

__block modifier creates unreadable memory

Now Im developing a banner showing feature in iOS
It's a singleton which shows banner on the upper part of screen when user logs in.
It's basically a shared view with a class method showWithName...
#interface XXUserWelcomeBanner ()
{
UIImageView *logoView;
UILabel *textLabel;
CGFloat _width;
}
#end
When user calls, it creates a UIImageView and a UILabel to add on self. And animates itself onto the screen.
+ (XXUserWelcomeBanner *)shared {
static dispatch_once_t onceToken;
static XXUserWelcomeBanner *userWelcomBanner;
dispatch_once(&onceToken, ^{
userWelcomBanner = [[XXUserWelcomeBanner alloc] init];
});
return userWelcomBanner;
}
+ (void)showWithUserName:(NSString *)userID andLogo:(UIImage * _Nullable)logo {
dispatch_async(dispatch_get_main_queue(), ^{
[[self shared] createWithName:userID andLogo:logo];
});
}
So there's this other bug I just found that causes this method to be called twice and on the second time it shows only the UIImageView.
And I don't understand why that's happening.
Because the UIImageView and UILabel won't be created twice.
Here's more code.
#pragma mark - private
- (void)createWithName:(NSString *)name andLogo:(UIImage * _Nullable)logo {
self.opaque = NO;
self.alpha = 0.9;
self.backgroundColor = COLOR(0xfcfcfc);
// if (#available(iOS 13.0, *)) {
// self.backgroundColor = UIColor.secondarySystemBackgroundColor;
// }
CGSize labelSize = CGSizeZero;
if (logoView == nil) {
if (logo != nil) {
logoView = [[UIImageView alloc] initWithImage:logo];
[self addSubview:logoView];
} else {
NSString *imagePath = [XXUtility pathForResourceNamed:#"Logo" withExtension:#"png"];
UIImage *imageToAdd = [[UIImage alloc] initWithContentsOfFile:imagePath];
logoView = [[UIImageView alloc] initWithImage:imageToAdd];
[self addSubview:logoView];
}
}
if (textLabel == nil) {
textLabel = [[UILabel alloc] init];
NSString * labelString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(#"welcom banner", #"userInterface", [XXUtility bundleForStrings], #"Dear %#, welcome into game", #"user welcome banner text"), name];
labelSize = [labelString sizeWithAttributes:#{NSFontAttributeName : textLabel.font}];
textLabel.text = labelString;
[self addSubview:textLabel];
}
NSLog(#"XXUserWelcomeBanner Label size is %f x %f", labelSize.height, labelSize.width);
[self bannerSizeWithLabelSize:labelSize];
CGFloat bannerHeight = self.frame.size.height;
CGFloat bannerWidth = self.frame.size.width;
CGFloat logoHeight = bannerHeight * 0.45;
CGFloat logoWidth = logoHeight;
CGFloat logoXPos = (bannerWidth - logoWidth - labelSize.width) / 2;
CGFloat logoYPos = (bannerHeight - logoHeight) / 2;
CGFloat labelHeight = labelSize.height;
CGFloat labelWidth = labelSize.width;
CGFloat labelXPos = logoXPos + logoWidth + 5;
CGFloat labelYPos = (bannerHeight - labelHeight) / 2;
logoView.frame = CGRectMake(logoXPos, logoYPos, logoWidth, logoHeight);
textLabel.frame = CGRectMake(labelXPos, labelYPos, labelWidth, labelHeight);
[self bannerShow];
}
- (void)bannerSizeWithLabelSize:(CGSize)lSize {
_width = 40 + lSize.width + 5;
CGFloat height = 40;
CGFloat xPosition = ScreenWidth * 1/2 - _width * 1/2;
CGFloat yPosition = - height;
self.frame = CGRectMake(xPosition, yPosition, _width, height);
}
- (void)bannerShow {
UIViewController *vc;
if ([TOP_VIEWCONTROLLER respondsToSelector:#selector(topViewController)]) {
vc = [TOP_VIEWCONTROLLER topViewController];
} else {
vc = TOP_VIEWCONTROLLER;
}
[vc.view addSubview:self];
[UIView animateWithDuration:0.6 delay:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGFloat height = 40;
CGFloat xPos = ScreenWidth * 1/2 - self->_width * 1/2;
CGFloat yPos = height * 1/3 + KiPhoneXSafeAreaDValue;
self.frame = CGRectMake(xPos, yPos, self->_width, height);
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.6 delay:0.2 options:UIViewAnimationOptionCurveEaseIn animations:^{
CGFloat height = 40;
CGFloat xPosition = ScreenWidth * 1/2 - self->_width * 1/2;
CGFloat yPosition = - height;
self.frame = CGRectMake(xPosition, yPosition, self->_width, height);
} completion:^(BOOL finished) {
[self bannerDestroy];
self.alpha = 0;
}];
});
}];
}
- (void)bannerDestroy {
[logoView removeFromSuperview];
[textLabel removeFromSuperview];
logoView = nil;
textLabel = nil;
[self removeFromSuperview];
}
- (void)dealloc
{
//
NSLog(#"uiview dealloc");
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
#end
Thanks in advance.
It is expected that createWithName:andLogo: is calling more than once in your app. I guess you call it with different arguments, that's why it should not be inside dispatch_once(&onceToken, ^{ });. But you can optimise XXUserWelcomeBanner's init:
- (instancetype)init {
self = [super init];
if (self) {
logoView = [[UIImageView alloc] init];
[self addSubview:logoView];
textLabel = [[UILabel alloc] init];
[self addSubview:textLabel];
}
return self;
}
In createWithName:andLogo: you will have the following:
if (logo != nil) {
logoView.image = logo;
} else {
NSString *imagePath = [XXUtility pathForResourceNamed:#"Logo" withExtension:#"png"];
UIImage *imageToAdd = [[UIImage alloc] initWithContentsOfFile:imagePath];
logoView.image = imageToAdd;
}
NSString * labelString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(#"welcom banner", #"userInterface", [XXUtility bundleForStrings], #"Dear %#, welcome into game", #"user welcome banner text"), name];
CGSize labelSize = [labelString sizeWithAttributes:#{NSFontAttributeName : textLabel.font}];
textLabel.text = labelString;

How change the page control indicator on image slide inside scrollview using NSTimer

Here is the image slider which works fine. When dragging manually the image changes and indicator also changes but when i add animation only image changes automatically not the indicator.
Instead of animating image i want to scroll image so that indicators change
just like it changes on dragging. Also i want to add timer to it.
#implementation DashboardViewController {
NSArray * animationArray;
}
#synthesize scroller = scroller;
#synthesize pageControl = pageControl;
-
(void) viewDidLoad {
[super viewDidLoad];
scroller.pagingEnabled = YES;
scroller.showsHorizontalScrollIndicator = NO;
scroller.delegate = self;
animationArray = [NSArray arrayWithObjects: [UIImage imageNamed: # "image1.jpg"],
[UIImage imageNamed: # "image2.jpg"],
[UIImage imageNamed: # "image3.jpg"],
[UIImage imageNamed: # "image4.png"],
[UIImage imageNamed: # "image5.jpg"],
nil
];
CGRect scrollFrame = CGRectMake(0, 0, self.view.frame.size.width, self.scroller.frame.size.height);
scroller.frame = scrollFrame;
self.scroller.contentSize = CGSizeMake(self.scroller.frame.size.width * animationArray.count, self.scroller.frame.size.height);
for (int i = 0; i < animationArray.count; i++) {
CGRect frame;
frame.origin.x = self.scroller.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scroller.frame.size;
UIImageView * imgView = [
[UIImageView alloc] initWithFrame: CGRectMake(self.scroller.frame.size.width * i, 0, self.scroller.frame.size.width, self.scroller.frame.size.height)
];
imgView.image = [animationArray objectAtIndex: i];
imgView.frame = frame;
imgView.animationImages = animationArray;
imgView.animationDuration = 8;
imgView.animationRepeatCount = 0;
[imgView startAnimating];
[self.scroller addSubview:imgView];
}
self.pageControl.currentPage = 0;
}
-
(void) scrollViewDidScroll: (UIScrollView * ) sender {
if (!pageControlBeingUsed) {
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = self.scroller.frame.size.width;
int page = floor((self.scroller.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
} -
(void) scrollViewWillBeginDragging: (UIScrollView * ) scrollView {
pageControlBeingUsed = NO;
}
-
(void) scrollViewDidEndDecelerating: (UIScrollView * ) scrollView {
[self setIndiactorForCurrentPage];
} -
(void) setIndiactorForCurrentPage {
uint page = scroller.contentOffset.x / scroller.frame.size.width;
[pageControl setCurrentPage: page];
}
-
(IBAction) changePage {
// Update the scroll view to the appropriate page
CGRect frame;
pageControl.currentPage = animationArray.count;
frame.origin.x = self.scroller.frame.size.width * self.pageControl.currentPage;
frame.origin.y = 0;
frame.size = self.scroller.frame.size;
[self.scroller scrollRectToVisible: frame animated: YES];
pageControlBeingUsed = YES;
}
-
(void) didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
self.scroller = nil;
self.pageControl = nil;
}
Instead of animating image i want to scroll image so that indicators change
just like it changes on dragging. Also i want to add timer to it.
I think you have to set your current page inside your for loop for automatic change pagecontrol with image.
//move this up
self.pageControl.currentPage = 0;
for (int i = 0; i < animationArray.count; i++) {
CGRect frame;
frame.origin.x = self.scroller.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scroller.frame.size;
UIImageView * imgView = [
[UIImageView alloc] initWithFrame: CGRectMake(self.scroller.frame.size.width * i, 0, self.scroller.frame.size.width, self.scroller.frame.size.height)
];
imgView.image = [animationArray objectAtIndex: i];
imgView.frame = frame;
imgView.animationImages = animationArray;
imgView.animationDuration = 8;
imgView.animationRepeatCount = 0;
[imgView startAnimating];
self.pageControl.currentPage = i; //set i to current page
[self.scroller addSubview:imgView];
}

UIImageView - Memory Issue

I am showing around 50 Images In a scroll view and I am facing the memory issue, every time the Controller loads the memory usage increases and it goes over 200MB in the Instruments App.
.h file of my custom class
#import
#class AnimalGrid;
#protocol AnimalGridViewDelegate <NSObject>
-(void)animalGrid:(AnimalGrid*)animalgridview tappedAtIndex:(int)index andName:(NSString*)animalName;
#end
#interface AnimalGrid : UIView
#property(nonatomic, strong) UIImage *animalImage;
#property(nonatomic, copy) NSString *animalName;
#property(nonatomic) int animalTag;
#property (nonatomic, assign) id <AnimalGridViewDelegate> Delegate;
-(id)initGridWithFrame:(CGRect)frame andAnimalImage:(UIImage*)image andAnimalName:(NSString*)animalname andTag:(int)tag;
-(void)setFont:(UIFont*)font;
#end
.m file of my Custom Class
#import "AnimalGrid.h"
#implementation AnimalGrid
{
UIImageView *_contentView;
UILabel *_accessoryView;
UIImage *displayImage;
NSString *displayText;
}
#synthesize Delegate = _Delegate;
-(id)initGridWithFrame:(CGRect)frame andAnimalImage:(UIImage*)image andAnimalName:(NSString*)animalname andTag:(int)tag
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.animalName = animalname;
self.animalTag = tag;
displayImage = image;
displayText = animalname;
CGFloat contentHeight = frame.size.height * 0.8;
if ( animalname == nil || animalname.length == 0)
contentHeight = frame.size.height;
CGFloat margin = 0;
if (displayImage.size.height < contentHeight)
{
margin = contentHeight - displayImage.size.height;
}
UIView *placeholderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, contentHeight)];
placeholderView.backgroundColor = [UIColor clearColor];
if (image.size.height > placeholderView.frame.size.height)
{
_contentView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, placeholderView.frame.size.height)];
}else if (image.size.height < placeholderView.frame.size.height){
CGFloat margin = placeholderView.frame.size.height - image.size.height;
_contentView = [[UIImageView alloc]initWithFrame:CGRectMake(0, margin, frame.size.width, placeholderView.frame.size.height - margin)];
}
_contentView.backgroundColor = [UIColor clearColor];
_accessoryView = [[UILabel alloc]initWithFrame:CGRectMake(0, frame.size.height * 0.7, frame.size.width, frame.size.height-frame.size.height * 0.7)];
_accessoryView.numberOfLines = 2;
_accessoryView.backgroundColor = [UIColor clearColor];
_accessoryView.font = [UIFont fontWithName:#"HFFAirApparent" size:23];
_accessoryView.textColor = [UIColor orangeColor];
[placeholderView addSubview:_contentView];
[self addSubview:placeholderView];
if ( animalname != nil || animalname.length > 0)
[self addSubview:_accessoryView];
if(image)
_contentView.image = image;
_contentView.contentMode = UIViewContentModeScaleAspectFit;
_accessoryView.text = animalname;
_accessoryView.numberOfLines = 0;
[_accessoryView sizeToFit];
CGFloat w = self.bounds.size.width/2;
_accessoryView.frame = CGRectMake(w - (_accessoryView.frame.size.width/2), _accessoryView.frame.origin.y, _accessoryView.frame.size.width, _accessoryView.frame.size.height);
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapedOnImage:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[_contentView addGestureRecognizer:singleTap];
_contentView.userInteractionEnabled = YES;
self.userInteractionEnabled = YES;
}
return self;
}
-(void)setFont:(UIFont*)font
{
_accessoryView.font = font;
}
-(void)tapedOnImage:(id)sender
{
if ([_Delegate respondsToSelector:#selector(animalGrid:tappedAtIndex:andName:)]) {
[_Delegate animalGrid:self tappedAtIndex:self.animalTag andName:self.animalName];
}
}
#end
And this is how I am creating the Object of my custom class and showing them over Scroll View, In my Controller file I am using following methods
-(void)setGridAnimals
{
#try {
[self creatingGridsWithCompletion:^(BOOL done, NSMutableArray *arr)
{
for (int i = 0; i < arr.count; i++)
{
UIView *gView = [arr objectAtIndex:i];
[gridScrollView addSubview:gView];
}
gridScrollView.scrollEnabled = YES;
gridScrollView.pagingEnabled = YES;
gridScrollView.bounces = YES;
gridScrollView.contentSize = CGSizeMake(gridScrollView.frame.size.width, gridScrollView.frame.size.height*arr.count);
[aView hideIndicatorView:YES];
}];
}
#catch (NSException *exception) {
NSLog(#" Exception is = %# ",exception.description);
}
#finally {
}
}
-(void)creatingGridsWithCompletion:(completionBlock)complete
{
#autoreleasepool {
NSMutableArray *pageArray = [NSMutableArray new];
int rowcount = 3;
int columncount = 5;
CGFloat width = gridScrollView.frame.size.width/columncount - 5;
CGFloat height = gridScrollView.frame.size.height/rowcount - 5;
int pagecount = gridAnimalArray.count/(rowcount*columncount);
if (gridAnimalArray.count%(rowcount*columncount))
{
pagecount += 1;
}
//pagecount = 1;
int x = 0;
for (int i = 0; i < pagecount; i++)
{
UIView *page = [[UIView alloc]initWithFrame:CGRectMake(0, x, gridScrollView.frame.size.width, gridScrollView.frame.size.height)];
for (int j = 0; j < rowcount; j++)
{
for (int k = 0; k < columncount; k++)
{
int tag = (i*(rowcount*columncount))+(j*columncount)+k+1;
if (tag > gridAnimalArray.count)
{
break;
}
YEAnimal *animal = [gridAnimalArray objectAtIndex:tag-1];
NSString *name = [[animal.GridName componentsSeparatedByString:#"."] objectAtIndex:0];
UIImage *gridImg;
if ([[NSFileManager defaultManager]fileExistsAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"png"]])
{
NSString * imgpath= [ [ NSBundle mainBundle] pathForResource:name ofType:#"png"];
gridImg=[UIImage imageWithContentsOfFile: imgpath];
}else if ([[NSFileManager defaultManager]fileExistsAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"jpg"]])
{
NSString * imgpath= [ [ NSBundle mainBundle] pathForResource:name ofType:#"jpg"];
gridImg=[UIImage imageWithContentsOfFile: imgpath];
}
AnimalGrid *grid = [[AnimalGrid alloc]initGridWithFrame:CGRectMake((k * width)+5, (j*height), width, height) andAnimalImage:gridImg andAnimalName:animal.SpeciesName andTag:tag];
grid.backgroundColor = [self getColor];
[grid setDelegate:self];
[page addSubview:grid];
}
}
[pageArray addObject:page];
x += gridScrollView.frame.size.height;
}
complete(YES,pageArray);
}
}
This is how the Grid Looks : http://s17.postimg.org/x9xdfl4j3/i_OS_Simulator_Screen_Shot_Mar_3_2015_1_54_45_P.png
So, far I came to know that I should use [UIImage imageWithContentsOfFile:] instead of [UIImage imageNamed:] to load the image, but still it does not help me.
Is there any way that I can free the Memory by releasing the ImageViews
Thanks.
There are several aproaches possible, depending on the layout of your view. How does it look like?
However, you can use a table to display the pictures in or use a collection. Actually a collection is made for that.
If you want to stick with the scroll view, then these 50 views are hardly visible at once. You could actually set their images just before the each view comes into sight and remove the images (set .image to nil) when the view moves off screen.
An App that I made recenly it is designed in a way that only one and exactly one of the images is visible full screen. When the user scrolls its neighbour image comes into view. (paging mechanism)
In my view the number of possible views is inpredictable. Although that is achievable with a collection too, I went for a scroll view.
In that view I designed the hosting scroll view in a way that it is just large enough to hold three views. The middle view of them is visible on screen. (Which exceptions applied when the very first or last - if any - view is displayed).
When the users scrolls just far enough for the neighbour view to be fully visible then I reset the scrollview so that its middle is visible again, move the view to the middle that just became visible, move the formerly visible view to the outer end and fill the other incoming end with a new view.
That allows for endless scrolling with not need to hold more than 4 images in memory at once. (4: the one that just moved off, the two that are shifted within the scroll view and the new one are allocated within the same method at a time.)

Zooming UIImageView in a UIScrollView acts weirdly

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 :)

Sample code for ios6 based scrollview with pagination and zooming features for an array of images

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.

Resources