Xcode: UIScrollView with Paging Tweak - ios

I found a great tutorial online that shows you how to page through different pages, well kind of. Here is the link to the tutorial here: http://www.iosdevnotes.com/2011/03/uiscrollview-paging/
The question I have is, how can I tweak his code so that instead of loading "colors" it loads different independent views or view controller files I create? Like I see it loads it's information from the NSArray, but how do you code it so that you include views or view controllers inside that array, and do you have to add anything else special to make this happen?
Here is the code I'm referring to from his tutorial which is in the implementation file:
- (void)viewDidLoad {
[super viewDidLoad];
pageControlBeingUsed = NO;
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor], [UIColor blueColor], nil];
for (int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
subview.backgroundColor = [colors objectAtIndex:i];
[self.scrollView addSubview:subview];
[subview release];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * colors.count, self.scrollView.frame.size.height);
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = colors.count;
}

As a quick thought, you could try creating an array or similar of views instead of colours.
Then in the for loop get the views out of the array and use this in the addSubView: message.
This would work for a few views but would not scale well.
I do something similar to this but with UIImageViews to be able to scroll a few images.

Related

Nested UIScrollView not paging for Internal UIScrollView

I am trying to build a screen which has 2 UIScrollViews, 1 Main Scroll View which is used as a container and to scroll vertically (this is working fine).
Inside the Main Scroll View, is a second scroll view, which is used for display different images. This needs to be scrolled horizontally so it will page to other images which will also update content details displayed in the Main Scroll View.
When attempting to get these two features to work together, the paging functionality does not work, however if it is outside the main scroll view it will work but will not scroll with the rest of the content.
The Image Scroll View is being detected in the events as well, but doesn't show any ability to scroll.
Below are my 3 functions which are performing the work.
- (void)viewDidLoad
{
[super viewDidLoad];
// Set up the Image Scroll View
ImageScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0, screenWidth, homeImage)];
ImageScrollView.scrollEnabled = YES;
ImageScrollView.userInteractionEnabled=YES;
[ImageScrollView setPagingEnabled:YES];
[ImageScrollView setAlwaysBounceVertical:NO];
ImageScrollView.delegate = self;
// Set up the image array
NSArray *imagesArray = [NSArray arrayWithObjects:#"staff1big.png", #"staff2big.png", #"staff3big.png", nil];
// Create each image subview
for (int i = 0; i < [imagesArray count]; i++)
{
CGFloat xOrigin = i * ImageScrollView.frame.size.width;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(xOrigin, 0, ImageScrollView.frame.size.width, ImageScrollView.frame.size.height)];
[imageView setImage:[UIImage imageNamed:[imagesArray objectAtIndex:i]]];
[ImageScrollView addSubview:imageView];
}
// Set the Content Size and Offset
[ImageScrollView setContentSize:CGSizeMake(ImageScrollView.frame.size.width * [imagesArray count], ImageScrollView.frame.size.height)];
[ImageScrollView setContentOffset:CGPointMake(screenWidth*currentIndex, 0)];
// Get the staff object
dataSource = [DataSource dataSource];
NSArray *staffArray = [dataSource getStaff];
// Setup the Pager Control
self.pageControl = [[UIPageControl alloc] init];
NSInteger placement = (screenWidth/2)-50;
self.pageControl.frame = CGRectMake(placement, homeImage-30, 100, 20);
self.pageControl.numberOfPages = [staffArray count];
self.pageControl.currentPage = currentIndex;
[self setStaff:staffArray[currentIndex]];
// Add the Main Scroll View
MainScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, screenHeight)];
MainScrollView.delegate = self;
MainScrollView.scrollEnabled = YES;
MainScrollView.userInteractionEnabled=YES;
// Add each object to the correct scroll view
[ImageScrollView addSubview:self.pageControl];
[MainScrollView addSubview:ImageScrollView];
[MainScrollView addSubview:lineView];
[self setDisplayContent]; // note the MainScrollView is added to the self.view in this method along with setting the content size to screenWidth and height is determine by the generated content.
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat pageWidth = ImageScrollView.frame.size.width;
float fractionalPage = ImageScrollView.contentOffset.x / pageWidth;
NSInteger page = lround(fractionalPage);
currentIndex = page;
self.pageControl.currentPage = page;
[self displayContent];
}
- (void)displayContent
{
int i = currentIndex;
if (i < 0)
{
i = 0;
} else if (i > 2) {
i = 2;
}
[self setStaff:allStaff[i]];
// remove all from view
for(UIView *subview in [MainScrollView subviews]) {
[subview removeFromSuperview];
}
NSArray *imagesArray = [NSArray arrayWithObjects:#"staff1big.png", #"staff2big.png", #"staff3big.png", nil];
for (int i = 0; i < [imagesArray count]; i++)
{
CGFloat xOrigin = i * ImageScrollView.frame.size.width;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(xOrigin, 0, ImageScrollView.frame.size.width, ImageScrollView.frame.size.height)];
[imageView setImage:[UIImage imageNamed:[imagesArray objectAtIndex:i]]];
[ImageScrollView addSubview:imageView];
}
[ImageScrollView setContentSize:CGSizeMake(ImageScrollView.frame.size.width * [imagesArray count], ImageScrollView.frame.size.height)];
[ImageScrollView setContentOffset:CGPointMake(screenWidth*currentIndex, 0)];
// Get the staff
dataSource = [DataSource dataSource];
NSArray *staffArray = [dataSource getStaff];
self.pageControl = [[UIPageControl alloc] init];
NSInteger placement = (screenWidth/2) - 50;
self.pageControl.frame = CGRectMake(placement, homeImage-30, 100, 20);
self.pageControl.numberOfPages = [staffArray count];
self.pageControl.currentPage = currentIndex;
[self setStaff:staffArray[currentIndex]];
[ImageScrollView addSubview:self.pageControl];
[MainScrollView addSubview:ImageScrollView];
[MainScrollView addSubview:lineView];
[self setDisplayContent];
}
I do need to refactor some of the code to make more efficient, but at this stage I am just trying to get the horizontal paging to scroll horizontally.
If anyone would possibly be able to help point me in the right direction as to how to fix this issue, it would be greatly appreciated.
Here is a visual of the UI that I am trying to keep but have the image scroll horizontal.
Staff Display

Using Scroll View and Page Controller for tutorial screen

I want to make a tutorial screen and have put a scroll view and a Page Controller. I want to put image so that the user can swipe through the tutorial screen on first boot. However I can put labels like this
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
float defWidth = _scrollView.frame.size.width;
float defHeight = _scrollView.frame.size.height;
_scrollView.pagingEnabled = YES;
_scrollView.contentSize = CGSizeMake(defWidth * _pageControl.numberOfPages, _scrollView.frame.size.height);
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.scrollsToTop = NO;
_scrollView.bounces = NO;
_scrollView.delegate = self;
UIColor *bgColor[] = {[UIColor orangeColor],[UIColor brownColor],[UIColor grayColor],[UIColor darkGrayColor],[UIColor blackColor]};
for (int i = 0; i < _pageControl.numberOfPages; i++) {
UILabel *label = [[UILabel alloc] init];
float x = (defWidth * (float)i);
label.frame = CGRectMake(x, 0.0f, defWidth, defHeight);
label.backgroundColor = bgColor[i];
label.textColor = [UIColor whiteColor];
label.font = [UIFont fontWithName:#"AppleGothic" size:20];
NSLog(#"%d,%f", i, x);
label.text = [NSString stringWithFormat:#"hogehoge%d", (i + 1)];
NSLog(#"%#", label.text);
[_scrollView addSubview:label];
}
}
But I cannot put images instead of labels. How can I put images in the scroll view so that it will be a slide show of few pictures?
to create a sliding image show so the user can swipe through the the images or images of the tutorials as you say, i find ray's tutorial very helpful. it also comes with the project sample code that has four different ways of sliding images. here is the link for that tutorial which is going to help you understand how it can be done.
http://www.raywenderlich.com/10518/how-to-use-uiscrollview-to-scroll-and-zoom-content
in adition if you want to just have a slide show there is a great control done by kirualex that is a nifty control and you can download it here:
https://github.com/kirualex/KASlideShow
i could have done a run down of these methods but it would be very lengthy and i think by going through the tutorial and looking at the codes you will understand this subject much better.

UIPageControl and UIScrollView during landscape orientation

I am having problems with UIPageControl and UIScrollView.
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", nil];
for (int i = 0; i < [imageArray count]; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
[self.scrollView addSubview:imageView];
}
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}
#pragma mark - UIScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
The problem occurs when I change the orientation to landscape. image1.jpg shares the view with image2.jpg, which is not what I want.
But if I change the orientation back to portrait, the view is doing fine.
The problem is that the page size has changed (i.e. the width of the scrollview) but the width of the images hasn't changed.
One solution would be that when you rotate you re-do the computation that sizes the UIImageViews and computes the scrollview's contentSize. That is, move the for loop and assignment to scrollView.contentSize out of viewDidLoad and into a private method, say sizeImagesAndSetContentSize. Call that from willAnimateRotationToInterfaceOrientation:duration:
Something like the following:
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", nil];
[self sizeImagesAndSetContentSize];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}
- (void) sizeImagesAndSetContentSize {
for (int i = 0; i < [imageArray count]; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
[self.scrollView addSubview:imageView];
}
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
[self sizeImagesAndSetContentSize];
}
By the way, viewDidLoad is not the place to do geometry calculations -- like things that involve the frame of the scrollView because the geometry isn't set then. in viewDidLoad the sizes of things are the sizes that they were in the storyboard (or nib) and haven't been adjusted to the actual device and the actual orientation. If it works for you so far, it is because you start out in the orientation that the storyboard is laid out. Probably the best place to do that, and thus to call sizeImagesAndSetContentSize is in the method viewDidLayoutSubviews. In fact, if you move the call there, you won't need to explicitly call it on rotation because when rotation occurs the view lays out the subviews and viewDidLayoutSubviews will get called automatically. So I'd suggest:
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", nil];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}
- (void) sizeImagesAndSetContentSize {
for (int i = 0; i < [imageArray count]; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
[self.scrollView addSubview:imageView];
}
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
}
- (void)viewDidLayoutSubviews {
[self sizeImagesAndSetContentSize];
}
Addendum:
ADDING TEXT TO THE IMAGE:
To add text on top of the image -- you could add a text element, like a label, "on top" of the scroll view. Consider the screenshot from an app below. In the top-level Cloud Slide Show View there is a full-sceen Scroll View and at the same hierarchy level but further down the list of subviews (and thus "on top" of the scrollview) are a label for a description, 3 buttons, and a UIPageControl. The label is set to resize on rotation via struts and springs (this runs on iOS 5) so you don't have to mess with it.
This application changes the description label to match the image that is currently displayed in the scrollview page -- so it has to detect page changes. This app does that in the scroll view delegate protocol method scrollViewDidEndDecelerating: where it decides which image is on-screen when scrolling stops and fills-in the correct description.
An alternative would be to put put a label on top of each UIImage, but that is messy which is why my app doesn't do that. You'd probably want a view to contain both the UIImage and a label so that repositioning the label would make sense. Probably create that in code... For me, too much trouble.
As an aside, I should say that using page mode in a scroll view is probably not the "best practice" way to do this anymore. I'm converting my app to use a UIPageViewController which should simplify the app. For instance, you don't have to do the resize-on-rotate thing since the individual child VCs of the UIPageViewControl can resize themselves automatically.

How to add images dynamically inside UIScrollView in iPhone

In my iPhone app i am getting around 300 images url from web server, i need to display all the images on UIScrollView in 3x3 row and column format. Once i get all the url from web server, convert them into UIImage and display image. Everything works fine, however after loading 200 images app crashes, I have read a lot its some thing memory kind of issue with UIScrollView, so how can we work on this. I have read apple documentation also, however its not clear.
here is the solution, i've implemented it in my app. Here is my code
sv_port is my scrollView.
Download SDWebImageDownloader class files and import it in your project. Add relevant framework in your project. like imageIO.framework, QuartzCore
Now, in your .m add
#import "UIImageView+WebCache.h"
#import "SDImageCache.h"
#import "SDWebImageCompat.h"
//function for adding image in your scrollview
-(void)populateImages
{
int x=6,y=6;
for(UIView * view in sv_port.subviews){
if([view isKindOfClass:[UIImageView class]])
{
[view removeFromSuperview]; view = nil;
}
if([view isKindOfClass:[UIButton class]])
{
[view removeFromSuperview]; view = nil;
}
}
for(int p=0;p<[Manufacturer count];p++)
{
UIButton *btnImageButton = [UIButton buttonWithType:UIButtonTypeCustom];
btnImageButton.frame = CGRectMake(x,y,70,70);
[btnImageButton setTag:p];
[btnImageButton addTarget:self action:#selector(nextview:) forControlEvents:UIControlEventTouchUpInside];
UIActivityIndicatorView *spinny = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinny.frame=CGRectMake(btnImageButton.frame.origin.x+25,btnImageButton.frame.origin.y+25, 20, 20);
spinny.backgroundColor=[UIColor clearColor];
[spinny startAnimating];
[sv_port setScrollEnabled:YES];
[sv_port addSubview:spinny];
UIImageView *asyncImageView = [[[UIImageView alloc] initWithFrame:btnImageButton.frame] autorelease];
asyncImageView.backgroundColor=[UIColor clearColor];
CALayer *layer;
layer = asyncImageView.layer;
layer.borderWidth = 0.5;
layer.cornerRadius = 5;
layer.masksToBounds = YES;
[asyncImageView setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#", yourUrl] ] placeholderImage:[UIImage imageNamed:nil] options:0 andResize:CGSizeMake(btnImageButton.frame.size.width,btnImageButton.frame.size.height) withContentMode:UIViewContentModeScaleAspectFit];
asyncImageView.contentMode = UIViewContentModeScaleAspectFit;
[sv_port addSubview:btnImageButton];
[sv_port addSubview:asyncImageView];
int imgCntPort=0;
imgCntPort=(sv_port.frame.size.width/(asyncImageView.frame.size.width));
////NSLog(#"imgport %d",imgCntPort);
if((p+1)%imgCntPort==0)
{
x=5;
y=y+80;
}
else
{
x=x+80;
}
}
sv_port.contentSize = CGSizeMake(0, y);
glob_x =0; glob_y =y;
}
hope it helps..
-(void)scrollimage
{
[AsyncImageLoader sharedLoader].cache = nil;
NSArray *viewsToRemove = [_scrollview_gallary subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
CGFloat width =_scrollview_gallary.bounds.size.width;
_scrollview_gallary.contentSize=CGSizeMake(width*[tmpArr count]-1, 360);
[_scrollview_gallary setShowsHorizontalScrollIndicator:NO];
[_scrollview_gallary setShowsVerticalScrollIndicator:NO];
int x=0;
int y=0;
for (int i=0; i<[tmpArr count]; i++)
{
AsyncImageView *imgv;
imgv =[[AsyncImageView alloc] initWithFrame:CGRectMake(x,y,width,360)];
imgv.contentMode = UIViewContentModeScaleToFill;
imgv.activityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
NSString *strimag=[NSString stringWithFormat:#"%#",[tmpArr objectAtIndex:i]];
imgv.imageURL=[NSURL URLWithString:[tmpArr objectAtIndex:i]];
[_scrollview_gallary addSubview:imgv];
x=x+width;
}
_pageview.numberOfPages=([tmpArr count]);
_scrollview_gallary.pagingEnabled = YES;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
int page;
CGFloat pageWidth = _scrollview_gallary.frame.size.width;
page = floor((_scrollview_gallary.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageview.currentPage = page;
}
I am getting an impression that you are downloading all 300 images together at once and keeping in memory and adding to the UIScrollView..
There is a technique called LazyLoad, means don't load all the images at once, load it in queue..
You can refer to this link http://lazyloadinginuiscrollviewiniphone.blogspot.in/2011/12/iphone-uiscrollview-lazy-image-loading.html it might help you to achieve things smoothly..
All the best.
If you want to display images in Gridview format then you can use PSTCollectionView. And for the lazy loading of the image you can use SDWebImage.
Are you releasing the elements that are not visible anymore? UITableView does that (that's why Sandro suggested it).
Your implementation of the UIScrollView should do that.
You can also consider UICollectionView or GMGridView for grid implementations that take this into consideration.
I do not see why what you're doing is a problem. May be you've got memory issue. In that case simulator and device result should be different.
If this is a memory problem, you should use a tableview instead of a scroll view. Put 3 images in each table view cell, and that should do the trick.
Cdt

How to properly implement multiple subview operations in IPAD

I am creating an iPAD app. I have got a MAIN VIEW controller which covers the whole screen. And I have another class DROP-DOWN Class which i will be using to mimic the functionality of a drop-down as in Windows OS components. For that I am using a simple button, a scroll view and a few labels. I instantiate an object of that class and use it in my MAIN VIEW controller. To create the effect of a drop down, I alter the instance's views frame on the touch up of the button in toggle mode.(Click once, frame increased, click again frame decreased).
The creation part was fine, and the function to toggle to worked fine when implemented on a single instance of the DROP DOWN object.
But the problem comes when I instantiate these objects thru a for loop. The views seem to just vanish on button click.
Here is the code within the dropdowns.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(10,54,100,50)];
scrollView.contentSize = CGSizeMake(50, 100);
for (int i = 0; i< 10; i++)
{
UILabel *newLable = [[UILabel alloc]initWithFrame:CGRectMake(0, 10*i, 100, 10)];
[newLable setFont:[UIFont boldSystemFontOfSize:10]];
newLable.text = [NSString stringWithFormat:#"Testing %i",i];
[scrollView addSubview:newLable];
}
scrollView.backgroundColor = [UIColor greenColor];
[self.view addSubview:scrollView];
}
-(IBAction)btnClicked:(id)sender
{
if (!isOpen) {
oldRect = self.view.frame;
CGRect newFrame = self.view.frame;
newFrame.size = CGSizeMake(190, 200);
NSLog(#"New Frame - %f,%f,%f,%f",self.view.frame.origin.x,self.view.frame.origin.y,self.view.frame.size.height,self.view.frame.size.width);
self.view.frame = newFrame;
}
else {
self.view.frame = oldRect;
NSLog(#"Old Frame - %f,%f,%f,%f",self.view.frame.origin.x,self.view.frame.origin.y,self.view.frame.size.height,self.view.frame.size.width);
}
isOpen = !isOpen;
}
And here is how this is called from the main view.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(drawTheFilters)];
}
-(void)drawTheFilters
{
for (int i = 0; i<5 ; i++) {
DropDownView *dropView = [[DropDownView alloc]initWithNibName:#"DropDownView" bundle:[NSBundle mainBundle]];
CGRect newFrame = dropView.view.frame;
newFrame.origin = CGPointMake(200*i, 200);
dropView.view.frame = newFrame;
[self.view addSubview:dropView.view];
NSLog(#"Frame %i, %f,%f,%f,%f",i,dropView.view.frame.origin.x,dropView.view.frame.origin.y,dropView.view.frame.size.height,dropView.view.frame.size.width);
}
}

Resources