This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
UIScrollView. Any thoughts on implementing “infinite” scroll/zoom?
I've got an UIScrollView and in it different images(about 30). I'd like to make it possible, when user reaches the last image to show the first one after it and so on. And I want to implement the same feature with the first image(to go to the last one).
I'd like to loop the images smoothly that user won't even notice that he is making another loop.
What is the best way to achieve this?
Thanks.
I've just done this. I have an array of images to add to the scrollview so I first of all add the last one to the scrollview, then run through the array to add all the images and then after that, add the first one from the array again.
Have a look through this code:
#import "MainViewController.h"
#import "MainView.h"
#define WIDTH_OF_SCROLL_PAGE 320
#define HEIGHT_OF_SCROLL_PAGE 352
#define WIDTH_OF_IMAGE 320
#define HEIGHT_OF_IMAGE 352
#define LEFT_EDGE_OFSET 0
#implementation MainViewController
#synthesize scrollView, slideImages;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
scrollView = [[UIScrollView alloc] init];
CGRect scrollFrame;
scrollFrame.origin.x = 0;
scrollFrame.origin.y = 0;
scrollFrame.size.width = WIDTH_OF_SCROLL_PAGE;
scrollFrame.size.height = HEIGHT_OF_SCROLL_PAGE;
scrollView = [[UIScrollView alloc] initWithFrame:scrollFrame];
scrollView.bounces = YES;
scrollView.pagingEnabled = YES;
scrollView.delegate = self;
scrollView.userInteractionEnabled = YES;
slideImages = [[NSMutableArray alloc] init];
[slideImages addObject:#"welcome-small.jpg"];
[slideImages addObject:#"football-small.jpg"];
[slideImages addObject:#"dancing-small.jpg"];
[slideImages addObject:#"celebration-small.jpg"];
//add the last image first
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[slideImages objectAtIndex:([slideImages count]-1)]]];
imageView.frame = CGRectMake(LEFT_EDGE_OFSET, 0, WIDTH_OF_IMAGE, HEIGHT_OF_IMAGE);
[scrollView addSubview:imageView];
[imageView release];
for (int i = 0;i<[slideImages count];i++) {
//loop this bit
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[slideImages objectAtIndex:i]]];
imageView.frame = CGRectMake((WIDTH_OF_IMAGE * i) + LEFT_EDGE_OFSET + 320, 0, WIDTH_OF_IMAGE, HEIGHT_OF_IMAGE);
[scrollView addSubview:imageView];
[imageView release];
//
}
//add the first image at the end
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[slideImages objectAtIndex:0]]];
imageView.frame = CGRectMake((WIDTH_OF_IMAGE * ([slideImages count] + 1)) + LEFT_EDGE_OFSET, 0, WIDTH_OF_IMAGE, HEIGHT_OF_IMAGE);
[scrollView addSubview:imageView];
[imageView release];
[scrollView setContentSize:CGSizeMake(WIDTH_OF_SCROLL_PAGE * ([slideImages count] + 2), HEIGHT_OF_IMAGE)];
[scrollView setContentOffset:CGPointMake(0, 0)];
[self.view addSubview:scrollView];
[self.scrollView scrollRectToVisible:CGRectMake(WIDTH_OF_IMAGE,0,WIDTH_OF_IMAGE,HEIGHT_OF_IMAGE) animated:NO];
[super viewDidLoad];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
int currentPage = floor((self.scrollView.contentOffset.x - self.scrollView.frame.size.width / ([slideImages count]+2)) / self.scrollView.frame.size.width) + 1;
if (currentPage==0) {
//go last but 1 page
[self.scrollView scrollRectToVisible:CGRectMake(WIDTH_OF_IMAGE * [slideImages count],0,WIDTH_OF_IMAGE,HEIGHT_OF_IMAGE) animated:NO];
} else if (currentPage==([slideImages count]+1)) {
[self.scrollView scrollRectToVisible:CGRectMake(WIDTH_OF_IMAGE,0,WIDTH_OF_IMAGE,HEIGHT_OF_IMAGE) animated:NO];
}
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[scrollView release];
[slideImages release];
[super dealloc];
}
#end
I then use scrollViewDidEndDecelerating to check where in the scroll the user is, and then jump them to the 2nd image or last but 1 image so they can scroll continuously... Sorry if I haven't explained it well - short of time! But this code is working fine for me on the device..
You can also have infinite scrolling without paging. Here is a great explanation at WWDC
Related
I have built an iPhone app with a page to show the contents of a .txt file in UIScrollView. For some reason my plain txt file causes the Scrollview to allow it to pan slightly to the left and right, I want it to be solid up down scrolling only.
Really need help with this one..
Thanks
#import "ITFAQController.h"
#import "FTCoreTextView.h"
#implementation ITFAQController
- (NSString *)textForView {
NSString *path = [[NSBundle mainBundle] pathForResource:#"faq" ofType:#"txt"];
NSError *error;
return [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
}
/*- (id)init
{
self = [super init];
if (self) {
// Custom initialization
[self showLogoInNavBar:YES];
}
return self;
}
*/
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
[self showLogoInNavBar:YES];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 330, (self.view.bounds.size.height - 90))];
[self.view addSubview:scrollView];
FTCoreTextView *ctView = [[FTCoreTextView alloc] initWithFrame:CGRectMake(10, 0, 300, scrollView.bounds.size.height)];
[ctView setText:[self textForView]];
FTCoreTextStyle *defaultStyle = [FTCoreTextStyle styleWithName:FTCoreTextTagDefault];
[defaultStyle setFont:kITDescriptionFont];
[defaultStyle setTextAlignment:FTCoreTextAlignementJustified];
[defaultStyle setParagraphInset:UIEdgeInsetsMake(5, 10, 0, 0)];
[ctView addStyle:defaultStyle];
FTCoreTextStyle *title = [FTCoreTextStyle styleWithName:#"title"];
[title setFont:kITTitleFont];
[title setColor:kITCellFontColor];
[title setParagraphInset:UIEdgeInsetsMake(10, 0, 5, 0)];
[ctView addStyle:title];
FTCoreTextStyle *bold = [FTCoreTextStyle styleWithName:#"bold"];
[bold setFont:[UIFont boldSystemFontOfSize:14]];
[ctView addStyle:bold];
FTCoreTextStyle *link = [FTCoreTextStyle styleWithName:FTCoreTextTagLink];
[link setColor:[UIColor blueColor]];
[ctView addStyle:link];
FTCoreTextStyle *bullets = [FTCoreTextStyle styleWithName:FTCoreTextTagBullet];
[bullets setBulletColor:kITCellFontColor];
[bullets setFont:kITItalicFont];
[bullets setParagraphInset:UIEdgeInsetsMake(0, 10, 0, 0)];
[ctView addStyle:bullets];
[ctView fitToSuggestedHeight];
[scrollView addSubview:ctView];
[scrollView setContentSize:[ctView suggestedSizeConstrainedToSize:CGSizeMake(scrollView.bounds.size.width, CGFLOAT_MAX)]];
//[ctView fitToSuggestedHeight];
//[ctView sizeToFit];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
CGRectMake(0, 0, 330, (self.view.bounds.size.height - 90))];
Replace 330 with 320. That should do the trick.
Edit: As written in the comments you should prefer self.view.bounds.size.width
The problem likely has to do with this line:
[scrollView setContentSize:[ctView suggestedSizeConstrainedToSize:CGSizeMake(scrollView.bounds.size.width, CGFLOAT_MAX)]];
I don't know what that method does, but it's probably giving you a contentSize with width > 320. Adding the following lines after that would fix it, I think:
if (scrollView.contentSize.width > self.view.bounds.size.width)
[scrollView setContentSize:CGSizeMake(self.view.bounds.size.width,scrollView.contentSize.height)];
I have main UIScrollView
mainScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 440)];
mainScrollView.pagingEnabled = YES;
imageScrollView.contentSize = CGSizeMake(320 * [myImage count], 440);
, which contain many sub UIScrollView, and each sub UIScrollView contains UIImageView
for (int i = 0; i < [myImage count]; i++)
{
imageView = [[UIImageView alloc] init];
[imageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
NSString *strimage = [myImage objectAtIndex:i];
NSURL *imageUrl = [NSURL URLWithString:strimage];
[imageView setImageWithURL:imageUrl placeholderImage:[UIImage imageNamed:#"Placeholder"]];
imageView.frame = CGRectMake(0, 0, 320, 440);
subScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(320 * i, 0, 320, 440)];
subScrollView.contentSize =
CGSizeMake(imageView.frame.size.width, imageView.frame.size.height);
subScrollView.pagingEnabled = YES;
subScrollView.showsHorizontalScrollIndicator = NO;
subScrollView.showsVerticalScrollIndicator = NO;
subScrollView.delegate = self;
subScrollView.bouncesZoom = YES;
subScrollView.clipsToBounds = YES;
subScrollView.maximumZoomScale = 3.0;
subScrollView.minimumZoomScale = 1.0;
subScrollView.zoomScale = 1.0;
[mainScrollView addSubview:subScrollView];
[subScrollView addSubview:imageView];
}
, and I implement
#pragma mark UIScrollViewDelegate methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return imageView;
}
The problem is when I zoom in UIImageView, assume that there are 3 sub UIScrollView, the last sub UIScrollView can zoom UIImageView normally but the other 2 when I zoom in it , it zooms sub UIScrollView instead of UIImageView, and it effect the last sub UIScrollView to be also zoom, Did I do anything wrong?
I suspect that in viewForZoomingInScrollView, it's zoom only last UIImageView (last sub UIScrollView).
Thank you in advance :)
Finally I figured it out, I just need to update my UIImageView when I zoom in specific UIImageView
In - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView like this:
#pragma mark -
#pragma mark UIScrollViewDelegate methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
for (UIImageView *iv in [scrollView subviews]) {
imageView = iv;
break;
}
return imageView;
}
Hope it may help someone, who struggle at this point as me, so you don't have to waste you time :).
I have a VC with a scrollview and pagecontrol implemented in it. It has three pages, which are all set up correctly and working fine. What I want to be able to do is have the VC load onto different pages depending on what button a user presses.
In the method the button calls I have
ViewController *vc = [[ViewController alloc] init];
vc.pageControl.currentPage = 2;
[self.navigationController pushViewController:vc animated:YES];
When the VC loads, it is loaded onto the correct page (as evidenced by the bubbles at the bottom of the page) but it is displaying the information from page[0]. If I start scrolling, it jumps back to page[0] no matter what I do.
My best guess is that I need to make the VC load to a specific x origin depending on what page I want to display, but I haven't been able to find anything related to this, so I'm quite lost.
Appreciate any help.
EDIT: Here is the code from the VC class. I didn't write this and it is my first time working with PageControl, so I'm trying to learn as I go.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.pagingEnabled = TRUE;
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.delegate = self;
firstPage = [[UIView alloc] init];
firstPage.tag = 0;
secondPage = [[UIView alloc] init];
secondPage.tag = 1;
thirdPage = [[UIView alloc] init];
thirdPage.tag = 2;
pages = [[NSArray alloc] initWithObjects:firstPage, secondPage, thirdPage, nil];
// Page Control
self.pageControl = [[UIPageControl alloc] init];
self.pageControl.numberOfPages = 3;
self.pageControl.currentPage = 0;
[self.view addSubview:self.scrollView];
[self.view addSubview:self.pageControl];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// First Page Setup
//
[self setupFirstPage];
// Second Page Setup
//
[self setupSecondPage];
// Third Page Setup
//
[self setupThirdPage];
// Frame everything
//
CGRect screenFrame = [[UIScreen mainScreen] applicationFrame];
CGRect navBarFrame = self.navigationController.navigationBar.frame;
self.scrollView.frame = CGRectMake(0, navBarFrame.size.height, 320, screenFrame.size.height - navBarFrame.size.height);
self.pageControl.frame = CGRectMake(0, screenFrame.size.height - 70, 320, 20);
firstPage.frame = CGRectMake(0, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
secondPage.frame = CGRectMake(320 * secondPage.tag, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
thirdPage.frame = CGRectMake(320 * thirdPage.tag, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
// Add Subviews
//
[self.scrollView addSubview:firstPage];
[self.scrollView addSubview:secondPage];
[self.scrollView addSubview:thirdPage];
self.scrollView.contentSize = CGSizeMake(320 * pages.count, self.scrollView.frame.size.height);
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
Try adding this function and calling it right after you set the page number:
- (void)pageSelectorChanged
{
CGRect frame = self.scrollView.frame;
frame.origin.x = frame.size.width * self.pageControl.currentPage;
frame.origin.y = 0;
[self.scrollView scrollRectToVisible:frame animated:NO];
}
I'm new to iPhone development and am working on this tutorial: http://www.iosdevnotes.com/2011/03/uiscrollview-paging/#comment-25166 - but am doing it in XCode 4.3.2. The example is pretty simple and I understand the code, but for some reason the scrollViewDidScroll function isn't firing for me in my ViewController.m (this thus doesn't change the Page Control pagination icons and update it to the current page). I placed an NSLog() in this function so I can tell if it is ever activating - but in my debug console I never see this text
The code from my ViewController.h is:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIScrollViewDelegate> {
UIScrollView* scrollView;
UIPageControl* pageControl;
}
#property (nonatomic, retain) IBOutlet UIScrollView* scrollView;
#property (nonatomic, retain) IBOutlet UIPageControl* pageControl;
#end
The code from my ViewController.m is:
#import "ViewController.h"
#implementation ViewController
#synthesize scrollView, pageControl;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
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;
}
//!!!!!!!!!!!!!!
//THIS IS WHERE MY PROBLEM IS -- THIS CODE DOESN'T SEEM TO EVER GET RUN EVEN WHEN I SCROLL!!!!!
//!!!!!!!!!!!!!
- (void)scrollViewDidScroll:(UIScrollView *)sender {
NSLog(#"this just fired");
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
self.scrollView = nil;
self.pageControl = nil;
}
- (void)dealloc {
[scrollView release];
[pageControl release];
[super dealloc];
}
#end
Any help would be greatly appreciated. I've been going back and forth from the downloadable file they have on the tutorial to my own over and over and over... I just feel like this thing should fire when I start scrolling and have no idea why it's not... Thanks in advance!
In Interface Builder you did not connect the scroll view’s “delegate” outlet to you view controller. But you can do it programmatically.
[self.scrollView setDelegate:self];
dianz is Right, Set your delegate.
[self.scrollView setDelegate:self];
OR
self.scrollView.delegate=self;
You forgot to set your delegate:
- (void)viewDidLoad {
[super viewDidLoad];
// This is what you forgot to do, set the delegate of the scrollView.
scrollView.delegate = self;
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];
}
Please make sure you have set the scrollview delegate to the viewcontroller you want.
You can also attach the delegate in the xib file, if the scrollview is added in the xib.
Also, don't forget to implement the scrollview delegate protocol UIScrollViewDelegate in the viewcontroller's header file.
For a exercise I created a project to experiment with the accelerometer functions of the iPhone. Right now when I run the app on my device it begins with a blank screen, shake the phone and a image is displayed.
I have to force close the app to clear the image. I was hoping someone could provide a solution that would make the image reset so I could repeat the process as many times as I wanted. (shake phone, display image, clear image) I'm thinking it needs a timer or something, not sure. Here is the source code. Thanks for taking the time to read and help.
// ViewController.m
// AccelTest
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
-(void)viewWillAppear:(BOOL)animated{
[self startAccel];
[self view];
}
-(void)viewWillDisappear:(BOOL)animated{
[self stopAccel];
[self view];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return NO;
}
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
double const kThreshold = 2.0;
// double const kThreshold = 2.0;
if ( fabsf(acceleration.x) > kThreshold
|| fabsf(acceleration.y) > kThreshold
|| fabsf(acceleration.z) > kThreshold){
[self.view addSubview:[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Icon.png"]]];
}
}
-(void)startAccel{
UIAccelerometer * accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = .25;
}
-(void)stopAccel{
UIAccelerometer * accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = nil;
}
#end
Here is how I would do it (without ARC) to tap the image to make it disappear.
Remove your line:
[self.view addSubview:[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Icon.png"]]];
And add these lines instead:
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Icon.png"]];
myImageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(rm:)];
[myImageView addGestureRecognizer:tapgr];
[tapgr release]; tapgr = nil;
[self.view addSubview:myImageView];
[myImageView release]; myImageView = nil;
Then add a method to the View Controller to remove the UIImageView when it is tapped.
-(void)rm:(UITapGestureRecognizer *)tapgr {
[tapgr.view removeFromSuperview];
}
When that Image is tapped once, the rm: method will be called which will remove the Image from self.view
Store a pointer to that image view somewhere and remove it from it's superview when you want to. Either with a timer, or user action, or something like that.