UIImageView - Memory Issue - ios

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

Related

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];
}

UIButton does not show up in custom UIView

I am banging my head on this but cannot figure out the issue.
I have a custom UIView: FKPoiMiniDetail and this is the implementation (FKPoiMiniDetail.m)
const static CGFloat kDialogLateralMargin = 15.0;
const static CGFloat kDialogHeight = 100.0;
const static CGFloat kDialogBottomMargin = 10.0;
const static CGFloat kBottomButtonHeight = 50.0;
#interface FKPoiMiniDetail ()
#property (nonatomic) float tabBarHeight;
#property (nonatomic) NSUInteger numberOfButtons;
#property (nonatomic) NSMutableArray *buttonBlocks;
#property (strong, nonatomic) PoI* myPoi;
#end
#implementation FKPoiMiniDetail
- (id) initWithTabBarHeight:(float)aTabBarHeight numberOfButtons:(NSUInteger)numButtons andPoi:(PoI*)aPoi{
if (self = [super init]) {
self.tabBarHeight = aTabBarHeight;
self.numberOfButtons = numButtons;
self.myPoi = aPoi;
self.buttonBlocks = [[NSMutableArray alloc] init];
[self configure];
}
return self;
}
and here I add the the buttons programmatically:
- (void) assignButton:(NSUInteger)index anImage:(UIImage*)image aText:(NSString*)text andCallback:(void(^)())cb{
if (index > self.numberOfButtons) {
ALog("ERROR: Index is greater than number of buttons");
return;
}
[self.buttonBlocks addObject:cb];
int imWidth = 20;
int imHeight = 20;
int imLabelSpacing = 8;
CGSize labelSize = [text sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];
CGFloat overallWidth = labelSize.width + imWidth + imLabelSpacing;
CGFloat test = self.bounds.size.width;
CGFloat buttonWidth = test / ((CGFloat) self.numberOfButtons);
int x = index * buttonWidth;
int y = self.frame.size.height - kBottomButtonHeight;
UIButton *buttonContainer = [[UIButton alloc] initWithFrame:CGRectMake(x,y,buttonWidth,kBottomButtonHeight)];
[buttonContainer addTarget:self action:#selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
buttonContainer.tag = index;
int firstImOrigin = x + (buttonWidth - overallWidth) / 2;
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(firstImOrigin, (kBottomButtonHeight - imHeight)/2, imWidth, imHeight)];
[iv setImage:image];
[buttonContainer addSubview:iv];
CGRect lR = CGRectMake(firstImOrigin + imWidth + imLabelSpacing, (kBottomButtonHeight - labelSize.height)/2, labelSize.width, labelSize.height);
UILabel *label = [[UILabel alloc] initWithFrame:lR];
label.text = text;
[buttonContainer addSubview:label];
[self addSubview:buttonContainer];
}
and this is the selector as the target of the UIButton:
- (void)buttonTouched:(UIButton*)b{
void (^ myblock)() = [self.buttonBlocks objectAtIndex:b.tag];
myblock();
}
Somewhere else I create this object and assign buttons like this:
FKPoiMiniDetail *md = [[FKPoiMiniDetail alloc] initWithTabBarHeight:self.tabBarController.tabBar.frame.size.height
numberOfButtons:2
andPoi:annotation.aPoI];
UIImage *im = [UIImage imageNamed:#"detail.png"];
[md assignButton:0 anImage:im aText:#"Dettagli" andCallback:^{
ALog("Button 0 pressed");
}];
[md assignButton:1 anImage:im aText:#"Naviga" andCallback:^{
ALog("Button 1 pressed");
}];
[self.mapView addSubview:md];
The second button is empty, but the strange thing is this: clicking inside the button the callback does execute...
Debugging seems useless:
The variables for the first button:
and the variables for the second button:
Any help is appreciated, thanks in advance!
"Decomposing" your FKPoiMiniDetail
FKPoiMiniDetail
View
buttonContainer1 - buttonContainer2 — buttonContainer3, etc
So what's inside a buttonContainer should always have the same "frames" whereas it's buttonContainer frame that changes, according to the number of buttons, since buttonContainer will be their superview.
So you declare:
int x = index * buttonWidth;
int y = self.frame.size.height - kBottomButtonHeight;
The'll help setting the buttonContainer frame.
But label and iv should not depend of them, only depend of the height and the width of [buttonContainer frame] since they seems to be centered.
I guess that from you code, if you do at the end [self addSubview:iv] and [self addSubview:label]; you may see them.
I've had problems with setting images on UIImageView in the past and my solution has been changing the order to this:
[buttonContainer addSubview:iv];
[iv setImage:image];
First adding he subview and then setting the image.

UIImageView animationDuration with fade effects

I'am searching for a (very) simple way to crossfade images within animationDuration time. I tried using NSTimer and some other stuff I found here, but no solution satisfy or helps me.
I create this stuff in a method (without comments) ...
- (UIImageView *)playSequence{
NSArray *structureArray = self.contenManager.ImageViewControllerSize;
NSMutableArray *structuresToPlay = [[NSMutableArray alloc] init];
// walk thru current collection
for(NSInteger i = 0; i < [self.currentCollection count]; i++)
{
NSInteger index = [[self.currentCollection objectAtIndex:i] integerValue];
[structuresToPlay addObject:[UIImage imageNamed:[structureArray objectAtIndex:index]]];
}
_playSequence = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
_playSequence.animationImages = structuresToPlay;
_playSequence.animationDuration = 5 * structuresToPlay.count;
[_playSequence startAnimating];
return _playSequence;
}
Here is the part where the method is called ...
- (void)justPlaySelection
{
UIView *justPlaySelection = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
// some background music code ...
// add play sequence
[justPlaySelection addSubview:self.playSequence];
// add close button
// { ... }
[self presentSemiView:justPlaySelection];
}
- (UIImageView *)playSequence
{
if (_playSequence == nil)
{
NSArray *structureArray = self.contenManager.ImageViewControllerSize;
NSMutableArray *structuresToPlay = [[NSMutableArray alloc] init];
// walk thru current collection
for(NSInteger i = 0; i < [self.currentCollection count]; i++)
{
NSInteger index = [[self.currentCollection objectAtIndex:i] integerValue];
[structuresToPlay addObject:[UIImage imageNamed:[structureArray objectAtIndex:index]]];
}
_playSequence = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
}
_playSequence.animationImages = structuresToPlay;
_playSequence.animationDuration = 5 * structuresToPlay.count;
[_playSequence startAnimating];
return _playSequence;
}
Here is another way to switch between to UIImageViews, it wouldn't be hard to add in the fade in and out.
- (void) performAnimationOfFrameAtIndex:(NSNumber*)indexNum
{
int index = indexNum.intValue;
BOOL completedSequece = index >= [self.frames count];
if (completedSequece) return;
UIImageView *imgIn;
UIImageView *imgOut;
if (index % 2 == 0) {
imgIn = self.animationImageViewOne;
imgOut = self.animationImageViewTwo;
}
else {
imgIn = self.animationImageViewTwo;
imgOut = self.animationImageViewOne;
}
imgIn.image = [self.frames objectAtIndex:index];
[self.view sendSubviewToBack:imgOut];
double speed = 0.1;
[self performSelector:#selector(performAnimationOfFrameAtIndex:) withObject:[NSNumber numberWithInt:index + 1] afterDelay:speed];
}
I created a project that does exactly what you describe (an effect like UIImageView animation sequence, but with a cross-fade between images.)
The project is on github: https://github.com/DuncanMC/Animate-Img
You need to have 2 image views.
The trick I use is to stack them on top of each other, and simply animate the top image view from alpha 1 -> 0, then switch images in the top image view and animate it from alpha 0 -> 1.0, so the new image is revealed.
The project demos a morph transition using only 5 frames. Each frame is marked with a number so you can see the frame changes. It has a switch which lets you turn cross-fading on or off.

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.

Adding subviews to a UIView at initialization

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?

Resources