How can I do a infinite scroller? - ios

I have a scrollview which automatically pass an image to another. It has 7 images, I want to that when you get to the seventh image pass me a first in the same way as the rest of transitions
and not moving quickly to the first position.
It is what is commonly called infinite scroll. Any help is appreciated.
- (void)viewDidLoad
{
for (int i = 1; i < 8 ; i++) {
UIImageView *imagen = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"C%d.png",i]]];
imagen.frame = CGRectMake((i-1)*580,35, 580, 300);
[_scroller addSubview:imagen];
_scroller.indicatorStyle = UIScrollViewIndicatorStyleWhite;
}
_scroller.delegate = self;
_scroller.contentSize = CGSizeMake(580*7, 300);
_scroller.pagingEnabled = YES;
if (scrollingTimer == nil)
{
scrollingTimer = [NSTimer scheduledTimerWithTimeInterval:6
target:self selector:#selector(scrollPages) userInfo:nil repeats:YES];
}
}
-(void)scrollToPage:(NSInteger)aPage{
float myPageWidth = [_scroller frame].size.width;
[_scroller setContentOffset:CGPointMake (aPage * myPageWidth, 0) animated:YES];
}
-(void)scrollPages{
[self scrollToPage:currentPage%7];
currentPage++;
}

One way to implement infinite scolling in a paging UIScrollView is to put a duplicate of the first page at the end of the scroll view (and vice versa, if you want infinite scrolling in both directions).
This article uses a UICollectionView instead, but the concept is the same for a paging UIScrollView. When the user scrolls to the duplicate of the first page at the end of the scroll view, set the content offset back to the actual first page (without animation, so the user doesn't know it's happened).
Here's some other resources I found useful:
Creating Circular and Infinite UIScrollViews
Apple has a sample project demonstrating infinite scrolling called StreetScroller
This answer to a similar question about infinite scrolling on SO

Related

Proper way to display UICollectionView with paging

I've read many many SO solutions about UICollectionView with paging. I myself also work with those solutions:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = self.collectionView.frame.size.width;
self.lbPaging.text = [NSString stringWithFormat:#"%d/%d", self.collectionView.contentOffset.x / pageWidth, totalPage];
}
OR
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat pageWidth = self.collectionView.frame.size.width;
self.lbPaging.text = [NSString stringWithFormat:#"%d/%d", self.collectionView.contentOffset.x / pageWidth, totalPage];
}
Yeah, this work fine most the time. But now I experienced something unexpected:
When user scroll fast enough, it'll get past the method above, which may cause the paging malfunction (like 15/13 at the last page, or showing 3/5 even though it's the first page).
Precisely for us developer or tester, to reproduce this bug, keep your finger touch on the scroll view and start to scroll. When you're nearly out of the edge (where normally, you'll release your finger), touch the other side of the edge and keep scrolling. It's easier to reproduce on real device.
So I'm asking if anyone know a proper way to display the page navigation? Well, without UIPageViewController (I'd like to display the numeric page, not the dots).
EDIT
I don't know if this problem is critical or not, but I think maybe it is, because I'm performing loading data when scrolling to next page. Recently, I've got crash at this (it's really hard to debug, like at which step it got crash):
[self.collectionView performBatchUpdates:^{
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
for (NSInteger index = 0; index < next2page.count; index++ ) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:(index + offset) inSection:0]];
}
[self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
} completion:nil];
Exception is something like this: Fail to insert items to index path in section 0 (has only 69 row, insert to indexPath:73)
hello i have create button in UICollectionView like load more data and function is given below:-
- (void)viewDidLoad {
//declare page inedex initial base 0.
self.pageIndex = 0;
[self loadMoreData];
}
after you call load more data function page index will increase here i want my page index like 0,20,40 so i have create self.pageIndex+= 20 u can change according to your want
-(void)loadMoreData
{
self.isFromMoreData = TRUE;
self.pageIndex+= 20;
NSLog(#"loadMoreData %d and self.index %d",self.pageIndex,self.index);
[self loadDataIntoCollectionView];//this function calls api
}
******************another way is given below*******************************
use UIRefreshControl.
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
refreshControl = refreshControl;
refreshControl.backgroundColor = [UIColor whiteColor];
refreshControl.tintColor = [UIColor grayColor];
[refreshControl addTarget:self action:#selector(loadMoreData) forControlEvents:UIControlEventValueChanged];
self.myCollectionView.pagingEnabled = YES;
[self.myCollectionView addSubview:refreshControl];
self.myCollectionView.alwaysBounceVertical = YES;
and call the same function loadMoreData that i have declare above.
Hope this help you...
please referred my image

iOS Image Viewer: Set Zoomed Image Back when Scrolling

I have a gallery view that I use to, surprise, view images. :) I wanted it to be very similar to the standard iOS photo app with the same basic functionality: scrolling through images that are zoomable. To do this, I use a regular scroll view which I then populate with other scroll views that contain the images. The basic idea is something like this:
And, I do the populating the following way:
for (int i = 0; i < [imageGalleryArray count]; i++)
{
UIScrollView *imageContainer = [[UIScrollView alloc] initWithFrame:CGRectMake((windowSize.width * i), 0.0f, windowSize.width, windowSize.height)];
imageContainer.contentSize = CGSizeMake(windowSize.width, windowSize.height);
imageContainer.showsHorizontalScrollIndicator = NO;
imageContainer.showsVerticalScrollIndicator = NO;
imageContainer.bouncesZoom = YES;
imageContainer.delegate = self;
imageContainer.minimumZoomScale = 1.0f;
imageContainer.maximumZoomScale = 3.0f;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, windowSize.width, windowSize.height)];
imageView.image = [[imageGalleryArray objectAtIndex:i] objectForKey:#"image"];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.tag = IMAGEZOOMVIEW;
imageView.opaque = YES;
[imageContainer addSubview:imageView];
[galleryScrollView addSubview:imageContainer];
// Add it to array for proper removal later
[contentScrollViewSubviewList addObject:imageContainer];
}
Quite simple and it works fine. The only problem arises when I am zoomed into an image and want to scroll to another. The expected behavior in this case would be to set the zoom level of all images back to their original size. I do this in the scrollViewDidEndDecelerating delegate method like so:
if (scrollView.tag == GALLERYSCROLLVIEW)
{
for (int i = 0; i < [contentScrollViewSubviewList count]; i++)
{
[[contentScrollViewSubviewList objectAtIndex:i] setZoomScale:1.0f animated:YES];
}
}
Unfortunately, it does not work (or rather not as intended). I did some poking around and found out that the image does indeed animate back to its original size when it is the active slide. To elaborate: if I zoom into an image and scroll to another, the image stays zoomed in. When I scroll back to it, that is when it zooms back to its original size. I, however, need the image to scale back as soon as I move away from it ().
just like in the iOS image viewer).
Frankly, I do not understand the whole zoom mechanic enough to find out how to do this properly, and the UIScrollView documentation is of no help either. Could someone show me how to solve my problem?

Scroll view is not smooth

I'm working in a project (iOS7 & ARC) in which, I want to display N number of images in the scroll view.These Images already stored into sandbox directory. My App has only landscape orientation I'm facing a problem that ScrollView is not smooth, it stuck after 2-3 times scroll
This is how I configure ScrollView
[self.containerScroll setAutoresizesSubviews:NO];
self.containerScroll.pagingEnabled = YES;
self.containerScroll.showsHorizontalScrollIndicator = NO;
self.containerScroll.showsVerticalScrollIndicator = NO;
self.containerScroll.scrollsToTop = NO;
self.containerScroll.maximumZoomScale = 5.0;
self.containerScroll.minimumZoomScale = 1.0;
self.containerScroll.delegate = self;
I'm maintaining only three Images in the scrollView at a time.
I'm loading Images in ScrollView in below method
-(void) loadScrollViewWithPage:(int) page{
if (page >= self.numberOfSlides)
return;
float image_width;
float image_height;
if(self.isFromListView){
if(IS_IPHONE5){
image_width = 568.0f;
image_height = 320.0f;
} else{
// iPhone retina-3.5 inch
image_width = 480.0f;
image_height = 320.0f;
}
}
else{
image_width = IMAGE_WIDTH;
image_height = IMAGE_HEIGHT;
}
CGFloat xPos = page * image_width;
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(xPos, 0.0f, image_width, image_height)];
imgView.tag = page;
NSString *imgPath = [self.storageDirPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%d%#", page, Image_Extension_JPG]];
NSFileManager *fileManager = [NSFileManager defaultManager];
__block UIImage *img = nil;
if(![fileManager fileExistsAtPath:imgPath]){
[imgView setContentMode:UIViewContentModeCenter];
img = [UIImage imageNamed:#"icon-loader.png"];
[imgView setImage:img];
}
else{
[imgView setContentMode:UIViewContentModeScaleAspectFit];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
img = [[UIImage alloc] initWithCGImage:[[UIImage imageWithData:[NSData dataWithContentsOfFile:imgPath]] CGImage] scale:1.0 orientation:UIImageOrientationUp];
dispatch_async(dispatch_get_main_queue(), ^{
[imgView setImage:img];
});
});
}
[self.containerScroll addSubview:imgView];
img = nil;
fileManager = nil;
imgView = nil;
}
and this how my ScrollView Delegate methods goes...
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
self.containerScroll.scrollEnabled = YES;
float page = self.containerScroll.contentOffset.x/self.view.frame.size.width;
showingSlide = (UInt16) roundf(page);
if(scrollView == self.containerScroll){
// switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = CGRectGetWidth(self.containerScroll.frame);
NSUInteger pageNo = floor((self.containerScroll.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:pageNo - 1];
[self loadScrollViewWithPage:pageNo];
[self loadScrollViewWithPage:pageNo + 1];
// a possible optimization would be to unload the views+controllers which are no longer visible
if(scrollView == self.containerScroll)
{
[self.previewTableView reloadData];
[self.previewTableView setContentOffset:CGPointMake(0, (page*220)+64) animated:NO];
[self.previewTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:page inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
[self updateSlideNumber];
[self flashSlideNumber];
}
//unload unnecessary imageviews from scroll view
for (UIView* view in self.containerScroll.subviews) {
if ([view isKindOfClass:[UIImageView class]] && view.tag != page && view.tag != page-1 && view.tag != page+1) {
[view removeFromSuperview];
}
}
}
}
Now the problem is smoothness of scrollView. When I start scrolling it scrolls fine but after 2 or 3 (or after any random number) pages scroll, it stuck and after trying 2-3 times only it moves again and I have to swipe hard to scroll. Thanks in advance.
I think it's a problem of memory somewhere Try #autorelease pool in your code.
using scrollview is not a good approach for showing images, I will recommend you to either use tableview or collevtionview for the same.
Your app's memory will keep on increasing with every scroll because scollview doesn't reuse the memory, on the other hand tableview and collectionview reuses the memory.
As the most effective way to become better, scroll really slowly (one at a time) while you monitor the memory usage in your App. You'll be able to watch it go up as each new image is added to the view, especially if you haven't done any optimisation on the images.
The other thing is that while your code does look like is deallocc-ing the images, you still need to remember that it still has to try to reload the images as you scroll. You're creating your images on the main thread so you're never going to get the smoothness of a UITableView. While I realise that you're creating your image views on async threads, the act of adding and scrolling them is still being taken care of by the mainthread.
I would suggest a UITableView to solve your problem, or a UICollectionView. If you're set on using the scrollview, I would suggest using a crusher of some type to get the image size to as small as possible, while still keeping quality decent.
If you need help on the TableView implementation you should find plenty of information around SO. Probably a good option if you still want it to look like a scroll view is just to make all seperators, headers etc to nil, and then just use lazy loading for the images.
You make two the mistake. At first: never use imageNamed for non graphics content (example, use imageNamed for button background). And second: you try load a big images in real time. So you scroll view have lags therefore. If you load all images before you show the scroll view the amination end lagging. But you can get memory warnings. So, you need optimise it. P.S. Sorry for my english

scrolling images in ios automatically

i have multiple images in array and a scrollView all the images should scroll automatically and when i clicked the image it should navigate to other view and display its details. after completing images scrolling from 0 to end of the array it should Again load 1st image i.e 0. in Animating i have lot of problem please help me....
iam using this code
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
- (void) onTimer {
// Updates the variable h, adding 100 (put your own value here!)
h += 100;
//This makes the scrollView scroll to the desired position
yourScrollView.contentOffset = CGPointMake(0, h);
}
try iCarousel library for scrolling the images, use autoscrolling example for the reference, set wrap property to yes if you want to repeat the content in the loop. add image array to carousel and implement carousel datasource and delegate methods to scroll the images. you can change different types like linear, rotary or overflow etc.
you can create image outlet after that
NSMutableArray *imgArray = [[NSMutableArray alloc]init];
for (int i =0; i<imgUrlArray.count; i++) {
NSString *imgStr = [serviceImgArray objectAtIndex:i];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgStr]];
[imgArray addObject:[UIImage imageWithData:imageData]];
}
yourimageView.animationImages = imgArray;
yourimageView.animationDuration = 10;
[yourimageView startAnimating];
that's it.

TiledScrollView doesn't display some tiles when scrolled to that area

I'm having some problems in my usage of the TiledScrollView from Apple (3_Tiling/​Classes/​TiledScrollView.m) where sometimes the tiles don't show as I scroll and other times they do show. To clarify, the problem I'm seeing is analogous to having a tableview list of 100 rows and only 10 rows are being displayed at a time. As you scroll through the list, sometimes one or more rows are blank and stay blank because once they're displayed on the screen there is no reloading to make sure the content is there. However once these blank rows go off screen and say you scroll back to them, they show up with the content.
It seems to be completely random with no discernible patterns to it's behaviour. I know the delegate method ((UIView *)tiledScrollView:(TiledScrollView *)tiledScrollView tileForRow:(int)row column:(int)column resolution:(int)resolution) is executing thru NSLog's.
My Question is:
1. Have you encountered this phenomenon and how did you solve it? or
2. My debugging skill are very rudimentary. If I wanted to isolate the problem by seeing whether the tile or subviews exists, or the imageView was not able to fetch the image or if its a rendering problem... how would I go about debugging this?
Note- the delegate method shown below is a stripped down version of the above tiledScrollView delegate method where I removed the row and resolution portions of the code since there is no need for it if I'm just scrolling horizontally.
- (UIView *)tiledScrollView:(HorizontalTiledScrollView *)tiledScrollView column:(int)column {
NSLog(#"+++ %s +++", __PRETTY_FUNCTION__);
// re-use a tile rather than creating a new one, if possible
UIView *tile = [tiledScrollView dequeueReusableTile];
if (!tile) {
// the scroll view will handle setting the tile's frame, so we don't have to worry about it
if (tiledScrollView == self.timeHour) {
tile = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, TIMEHOUR_COLUMN_WIDTH, TIMEHOUR_COLUMN_HEIGHT)] autorelease];
} else if (tiledScrollView == self.timeMinute) {
tile = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, TIMEMINUTE_COLUMN_WIDTH, TIMEMINUTE_COLUMN_HEIGHT)] autorelease];
}
// Some of the tiles won't be completely filled, because they're on the right or bottom edge.
// By default, the image would be stretched to fill the frame of the image view, but we don't
// want this. Setting the content mode to "top left" ensures that the images around the edge are
// positioned properly in their tiles.
[tile setContentMode:UIViewContentModeTopLeft];
}
for(UIView *subview in [tile subviews]) {
if (subview.tag != 3) {
[subview removeFromSuperview]; //remove all previous subviews in tile except tile annotation if present
}
}
UIImageView *imgView = [[UIImageView alloc] init];
UILabel *digitLabel;
// Add blank UIImageView as filler or UIImageView with PNG or UILabel if no PNG sized correctly and offsetted from tile's origin as subviews in the tile
if (tiledScrollView == self.timeHour) {
if (column < 1) {
imgView.frame = CGRectZero;
[tile addSubview:imgView];
[tile bringSubviewToFront:imgView];
} else {
int digitH = ((column - 1) % 12 + 1);
imgView.frame = CGRectMake(9, 0, 17, 21);
[imgView setContentMode:UIViewContentModeScaleToFill];
if ((imgView.image = [UIImage imageNamed:[NSString stringWithFormat:#"TimeHour_%02d.png", digitH]])) {
[tile addSubview:imgView];
[tile bringSubviewToFront:imgView];
} else {
// NSLog(#"No TimeHour_%02d.png", digitH);
digitLabel = [self makeDigitLabel:digitH frame:imgView.frame fontSize:14.0];
[tile addSubview:digitLabel];
[tile bringSubviewToFront:digitLabel];
}
}
} else if (tiledScrollView == self.timeMinute) {
// if (column % 2) {
// tile.backgroundColor = [UIColor redColor];
// } else {
// tile.backgroundColor = [UIColor blueColor];
// }
if (column < 1) {
imgView.frame = CGRectZero;
[tile addSubview:imgView];
[tile bringSubviewToFront:imgView];
} else {
int digitM = ((column - 1) % 60);
imgView.frame = CGRectMake(9, 0, 16, 15);
[imgView setContentMode:UIViewContentModeScaleToFill];
if ((imgView.image = [UIImage imageNamed:[NSString stringWithFormat:#"TimeMinute_%02d.png", digitM]])) {
[tile addSubview:imgView];
[tile bringSubviewToFront:imgView];
} else {
NSLog(#"No TimeMinute_%02d.png", digitM);
digitLabel = [self makeDigitLabel:digitM frame:imgView.frame fontSize:12.0];
[tile addSubview:digitLabel];
[tile bringSubviewToFront:digitLabel];
}
}
}
}
[imgView release];
NSLog(#"Tile: %d",[tile.subviews count]);
return tile;
}
Hope it's clear.
Thanks for helping,
Hiren
I finally solved this!
The issue wasn't in the code posted above. It was in the layoutSubviews method of Apple's TiledScrollView. There is a line of code that calculates the maximum column shown below (max row has a similar calculation)
int lastNeededCol = MIN(maxCol, floorf(CGRectGetMaxX(visibleBounds) / scaledTileWidth));
In my case, this formula calculates one extra column than is needed and this column tile ends up sitting off screen. This is fine when you're not scrolling around and setting animated to NO. But when you do scroll around and/or set animated to YES in scrollview setContentOffset method call, you will sometimes end up with a missing tile because of the delay due to animation or if you scrolled really slowly. The animation or moving really slowly causes the scrollview to detect there is a movement and thus calls the layoutSubviews method where a line of code checks to see which tiles are visible and drops non-visible tiles. Now if you did this just right, then the extra tile created earlier gets dropped because its still off-screen and is never created again until the tile has moved far enough off-screen triggering a reload of that tile.
The fix I did was to change the above line to:
int lastNeededCol = MIN(maxCol, floorf((CGRectGetMaxX(visibleBounds)-0.1) / scaledTileWidth));
This new formula calculates the right number of columns of tiles needed to be displayed on screen thereby eliminating the whole thing of dropping extra tiles sitting off-screen.
I create an example code on github that helps demonstrates this.
https://github.com/hmistry/TiledScrollViewDebug

Resources