I am using an iCarousel. The carousel contains about 10 images. While screen transitions the images get stuck to the next screen. I am using popToRootview while going back to the original screen. Does it have to do with the UIImage alloc ?
My Code:
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
return ([animals count]);
}
- (NSUInteger)numberOfVisibleItemsInCarousel:(iCarousel *)carousel
{
//limit the number of items views loaded concurrently (for performance reasons)
return 7;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index
{
NSString *filePath1 = [animals objectAtIndex:index];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath1];
//create a numbered view
UIView *view = [[UIImageView alloc] initWithImage:image];
view.frame = CGRectMake(0.0f, 0.0f, 230, 150);
return view;
}
- (NSUInteger)numberOfPlaceholdersInCarousel:(iCarousel *)carousel
{
//note: placeholder views are only displayed on some carousels if wrapping is disabled
return 0;
}
- (CGFloat)carouselItemWidth:(iCarousel *)carousel
{
//usually this should be slightly wider than the item views
return 240;
}
- (BOOL)carouselShouldWrap:(iCarousel *)carousel
{
//wrap all carousels
return wrap;
}
- (void)carouselDidEndScrollingAnimation:(iCarousel *)aCarousel
{
label.text = [descriptions objectAtIndex:aCarousel.currentItemIndex];
[_label1 setText:[NSString stringWithFormat:#"%ld/%lu", (long)aCarousel.currentItemIndex+1,(unsigned long)[animals count]]];
}
- (IBAction)btnBackAct:(UIButton *)sender
{
[carousel removeFromSuperview];
//[carousel scrollToItemBoundary];
[carousel scrollToItemAtIndex:0 animated:NO];
[self.navigationController popViewControllerAnimated:YES];
}
try:
- (IBAction)btnBackAct:(UIButton *)sender
{
[carousel scrollToItemAtIndex:0 duration:0.01];
[self performSelector:#selector(popMyView) withObject:nil afterDelay:0.02];
}
-(void)popMyView
{
[self.navigationController popViewControllerAnimated:YES];
}
Related
I have the following implementation. Each view has animation, but my problem is that all animation run no matter visible or invisible.I only want the currently seen(active) view animation works, other view animation are disabled unless until user scrolls it.
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view{
if (view == nil){
if(index==0)
{
view=[[UIImageView alloc]initWithFrame:CGRectMake(30, 30, 600, 600)];
((UIImageView *)view).animationImages=[NSArray arrayWithObjects:[UIImage imageNamed:#"Walking-1.png"],
[UIImage imageNamed:#"Walking-2.png"],
[UIImage imageNamed:#"Walking-3.png"],
[UIImage imageNamed:#"Walking-4.png"],
[UIImage imageNamed:#"Walking-5.png"],nil];
((UIImageView *)view).animationDuration = 1.5;
[((UIImageView *)view) startAnimating];
}
else if(index==1)
{
view=[[UIImageView alloc]initWithFrame:CGRectMake(30, 30, 600, 600)];
((UIImageView *)view).animationImages=[NSArray arrayWithObjects:[UIImage imageNamed:#"Biking-1.png"],
[UIImage imageNamed:#"Biking-2.png"],
[UIImage imageNamed:#"Biking-3.png"],
[UIImage imageNamed:#"Biking-4.png"],
[UIImage imageNamed:#"Biking-5.png"],nil];
((UIImageView *)view).animationDuration = 1.5;
[((UIImageView *)view) startAnimating];
}
view.contentMode = UIViewContentModeCenter;
[view.layer setMasksToBounds:YES];
}
return view;
}
iCarouselDelegate has a method carouselCurrentItemIndexDidChange you should do stop/start your animations there, but not in data source method. So save all your animations into array with an index equal to an item index and use currentItemIndex property of carousel in carouselCurrentItemIndexDidChange to start appropriate animation. You will need to store previous index somewhere in your class, to be able to stop previous animation, before start new one.
#import UIKit;
#interface CarouselController: UIViewController
#property(nonatomic, readonly) NSArray *carouselItems;
#property(nonatomic, assign) NSInteger lastItemIndex;
#end
#implementation CarouselController
- (instancetype)init {
self = [super init];
if (self) {
[self initializeCarouselContent];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initializeCarouselContent];
}
return self;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[self initializeCarouselContent];
}
return self;
}
- (void)initializeCarouselContent {
self.lastItemIndex = 0;
_carouselItems = #[[CarouselController activityAnimationView: #"Walking"], [CarouselController activityAnimationView: #"Biking"]];
UIImageView *currentAnimation = self.carouselItems[self.lastItemIndex];
[currentAnimation startAnimating];
}
+ (UIImageView *)activityAnimationView:(NSString *)activity {
UIImageView *result = [[UIImageView alloc]initWithFrame:CGRectMake(30, 30, 600, 600)];
NSMutableArray *activityImages = [[NSMutableArray alloc] init];
for (int activityIndex = 1; activityIndex <= 5; activityIndex ++) {
NSString *imageName = [NSString stringWithFormat:#"%#-%i.png", activity, activityIndex];
[activityImages addObject:[UIImage imageNamed:imageName]];
}
result.animationImages = activityImages;
result.animationDuration = 1.5;
result.contentMode = UIViewContentModeCenter;
[result.layer setMasksToBounds:YES];
return result;
}
- (NSInteger)numberOfItemsInCarousel:(iCarousel *)carousel {
return [_carouselItems count];
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *) view{
if (view == nil){
view = self.carouselItems[index];
}
return view;
}
- (void)carouselCurrentItemIndexDidChange:(iCarousel *)carousel {
if (carousel.currentItemIndex != self.lastItemIndex) {
UIImageView *currentAnimation = self.carouselItems[self.lastItemIndex];
[currentAnimation stopAnimating];
self.lastItemIndex = carousel.currentItemIndex;
currentAnimation = self.carouselItems[self.lastItemIndex];
[currentAnimation startAnimating];
}
}
#end
I'm using iCarousel to display images clicked by the user and are reloaded dynamically into the carousel. The whole setup works perfectly with default values for iCarouselOptionVisibleItems but images start to scale up when the total number of images exceeds 8.
TO try and fix the issue i increased the number of iCarouselOptionVisibleItems to 25 and that temporarily fixed the issue but i know thats not the best solution because the images will still scale after the number exceeds 25. Is there something I'm missing from the documentation that I'm missing?
Here's the code for my icarousel screen
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.carousel.delegate = self;
self.carousel.dataSource = self;
_carousel.type = iCarouselTypeCoverFlow2;
}
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
//return the total number of items in the carousel
return [images count];
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
//create new view if no view is available for recycling
if (view == nil)
{
//don't do anything specific to the index within
//this `if (view == nil) {...}` statement because the view will be
//recycled and used with other index values later
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)];
view.contentMode = UIViewContentModeScaleAspectFit;
((UIImageView *)view).image = [UIImage imageWithContentsOfFile:[images objectAtIndex:index]];
}
else
{
//get a reference to the label in the recycled view
view.contentMode = UIViewContentModeScaleAspectFit;
view = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[images objectAtIndex:index]]];
}
return view;
}
- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
{
if (option == iCarouselOptionSpacing)
{
return value * 1.1f;
}
else if (option == iCarouselOptionVisibleItems)
{
return 25;
}
return value;
}
Hi my requirement is showing image in scrollview along with that it should display next and previous button.User can scroll the image as well as use buttons to move images.
I have doubt about how to get the index/tag/imagename or anything else when user scroll based on that values buttons should be updated.
Here is my Code
[self arrayInitiaisation];
scrollview.pagingEnabled = YES;
scrollview.alwaysBounceVertical = NO;
scrollview.alwaysBounceHorizontal = YES;
NSInteger numberOfViews = 4;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i*320;
imageView.image = [UIImage imageNamed:[NSString stringWithFormat:#"image%d",i+1]];
imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.scrollview addSubview:imageView];
imageView.tag = i;
...
Array Initialisation method
-(void) arrayInitialisation {
count=0;
[super viewDidLoad];
images=[NSMutableArray arrayWithObjects : [UIImage imageNamed:#"image1.png"],
[UIImage imageNamed :#"image2.png"],
[UIImage imageNamed :#"image3.png"],
[UIImage imageNamed :#"image4.png"],nil];
[imageView setImage:[images objectAtIndex:0]];
NSLog(#"Array Length :%d",images.count);
[previous setEnabled:NO];
}
Action Methods
- (IBAction)nextButton:(id)sender {}
- (IBAction)prevButton:(id)sender {}
scroll is working correctly but when user clicks on prev and next image is not updating on 'imageview'
Here is my updated code
int count=0;
- (void)viewDidLoad
{
[super viewDidLoad];
[self arrayInitialisation];
[scrollview setScrollEnabled:YES];
self.scrollview =[[UIScrollView alloc] initWithFrame:CGRectMake(30 , 30, 320, 412)];
self.scrollview.delegate=self;
scrollview.pagingEnabled = YES;
scrollview.alwaysBounceVertical=NO;
scrollview.alwaysBounceHorizontal=YES;
scrollview.showsHorizontalScrollIndicator=NO;
NSInteger numberOfViews = 4;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i*320;
imageView = [[UIImageView alloc]initWithFrame:CGRectMake(xOrigin, 0, 320, 412)];
imageView.image = [images objectAtIndex:i];
imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.scrollview addSubview:imageView];
imageView.tag=i;
}
scrollview.contentSize = CGSizeMake(320*numberOfViews, 412);
scrollview.contentOffset = CGPointMake(0,0);
[self.view addSubview:self.scrollview];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(void) arrayInitialisation{
count=0;
[super viewDidLoad];
images=[NSArray arrayWithObjects : [UIImage imageNamed:#"image1.png"],
[UIImage imageNamed :#"image2.png"],
[UIImage imageNamed :#"image3.png"],
[UIImage imageNamed :#"image4.png"],nil];
[imageView setImage:[images objectAtIndex:0]];
NSLog(#"Array Length :%d",images.count);
[previous setEnabled:NO];
}
- (IBAction)nextButton:(id)sender {
if(count==0){
count++;
NSLog(#"Next Button : if Loop %d",count);
[imageView setImage:[images objectAtIndex:count]];
[previous setEnabled:YES];
}
else{
[previous setEnabled:YES];
count++;
NSLog(#"Next Button : else Loop %d",count);
if(count <images.count){
[imageView setImage:[images objectAtIndex:count]];
}
else{
[next setEnabled:NO];
}
}
}
- (IBAction)prevButton:(id)sender {
if(count==0){
//count--;
NSLog(#"prev Button : if Loop %d",count);
[imageView setImage:[images objectAtIndex:count]];
[previous setEnabled:NO];
[next setEnabled:YES];
}
else{
count--;
NSLog(#"prev Button : else Loop %d",count);
[imageView setImage:[images objectAtIndex:count]];
[previous setEnabled:YES];
//[next setEnabled:YES];
}
}
#pragma mark - ScrollView Delegate
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate: (BOOL)decelerate{
//NSLog(#"scrollViewDidEndDragging");
/* count=count+1;
NSLog(#"scrollViewDidEndDragging : %d",count);*/
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
//NSLog(#"scrollViewDidScroll");
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset NS_AVAILABLE_IOS(5_0){
//NSLog(#"scrollViewWillEndDragging");
count=count+1;
NSLog(#"scrollViewWillEndDragging : %d",count);
}
I have left and right button for scroll images, and imageview width is same.
I am writing code for scroll image at pressing button.
int pos=0;// ViewDiaLoad
- (IBAction)OnClickLeftButton:(UIButton*)sender
{
if(pos>0)
{
pos -=1;
[scrlDemandMenu scrollRectToVisible:CGRectMake(pos*scrlDemandMenu.frame.size.width, 0, scrlDemandMenu.frame.size.width, scrlDemandMenu.frame.size.height) animated:YES];
}
}
- (IBAction)OnClickRightButton:(UIButton*)sender
{
if(pos<numberOfImages)
{
pos +=1;
[scrlDemandMenu scrollRectToVisible:CGRectMake(pos*scrlDemandMenu.frame.size.width, 0, scrlDemandMenu.frame.size.width, scrlDemandMenu.frame.size.height) animated:YES];
}
}
Set scrollView Deleagte
Scroll View delegate Method
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
pos=scrollView.contentOffset.x/scrollView.frame.size.width;
}
You can use UIPageControl and set and get image according to page index. For more information check out this link
Try below piece of code. I hope it will help. Put this in your .h file
IBOutlet UIScrollView *scrollView;
IBOutlet UIPageControl *pageControl;
NSMutableArray *arrImages;
NSInteger page;
Now in your - (void)viewDidLoad put following lines.
int x = 30;
for (int i=0; i< arrImages.count; i++)
{
UIImageView *imgShow = [[UIImageView alloc]initWithFrame:CGRectMake(x+20, 50, self.view.frame.size.width-100, self.view.frame.size.height-180)];
imgShow.backgroundColor = [UIColor whiteColor];
imgShow.image = [UIImage imageNamed:[arrImages objectAtIndex:i]];
imgShow.contentMode = UIViewContentModeScaleAspectFit;
[scrollView addSubview:imgShow];
x = x+scrollView.frame.size.width;
}
scrollView.contentSize = CGSizeMake(5*self.view.frame.size.width, 0);
pageControl.backgroundColor = [UIColor clearColor];
pageControl.pageIndicatorTintColor = [UIColor grayColor];
pageControl.currentPageIndicatorTintColor = [UIColor blueColor];
[pageControl setTintAdjustmentMode:UIViewTintAdjustmentModeDimmed];
pageControl.numberOfPages = arrTitle.count;
pageControl.currentPage = 0;
implement scroll view delegate.
#pragma mark - ScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView1
{
CGFloat pageWidth = scrollView.frame.size.width;
float fractionalPage = scrollView.contentOffset.x / pageWidth;
page = lround(fractionalPage);
pageControl.currentPage = page;
}
- (IBAction)prevButton:(UIButton *)sender
{
page = page-1;
UIButton *btn = (UIButton *)[sender viewWithTag:sender.tag];
// write your code
for (id obj in btn.subviews)
{
if ([obj isKindOfClass:[UIImageView class]])
{
// write your code
}
else
{
// write your code
}
}
}
- (IBAction)nextButton:(UIButton *)sender
{
page = page+1;
UIButton *btn = (UIButton *)[sender viewWithTag:sender.tag];
// write your code
for (id obj in btn.subviews)
{
if ([obj isKindOfClass:[UIImageView class]])
{
// write your code
}
else
{
// write your code
}
}
}
I'm using Nick Lockwood's iCarousel for an iPad app. Right now it's just a carousel of 15 full screen images.
I setup a single view project, add the iCarousel view in storyboard, add it's view controller as a data source and put this code for the datasource methods:
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel {
return 15;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
UIImage* img = [UIImage imageNamed:[NSString stringWithFormat:#"image%d.jpg", index]];
UIImageView* imgView = [[UIImageView alloc] initWithImage:img];
return imgView;
}
This works, but the first time I scroll through all the items you can notice a little performance hit when a new item is being added to the carousel. This does not happen the second time I go through all the items.
You can see what I mean in this profiler screenshot. The peaks in the first half are for the first time I scroll through all the images, then I do it again and there are no peaks and no performance hit.
How can I fix this?
EDIT
I isolated one of the peaks on instruments and this is the call tree
EDIT
The code with jcesar suggestion
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray* urls_aux = [[NSMutableArray alloc] init];
for (int i = 0; i<15; i++) {
NSString *urlPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"captura%d", i] ofType:#"jpg"];
NSURL *url = [NSURL fileURLWithPath:urlPath];
[urls_aux addObject:url];
}
self.urls = urls_aux.copy;
self.carousel.dataSource = self;
self.carousel.type = iCarouselTypeLinear;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
if (view == nil) {
view = [[[AsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)] autorelease];
view.contentMode = UIViewContentModeScaleAspectFit;
}
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:view];
((AsyncImageView *)view).imageURL = [self.urls objectAtIndex:index];
return view;
}
I don't think this is a very orthodox solution, but it works.
What I do is use iCarousel's method insertItemAtIndex:animated: to add the images one at a time on setup. Performance gets hit then, but after that everything runs smoothly.
- (void)viewDidLoad
{
[super viewDidLoad];
self.images = [[NSMutableArray alloc] init];
self.carousel.dataSource = self;
self.carousel.type = iCarouselTypeLinear;
for (int i = 0; i<15; i++) {
UIImage* img = [UIImage imageNamed:[NSString stringWithFormat:#"captura%d.jpg", i]];
[self.images addObject:img];
[self.carousel insertItemAtIndex:i animated:NO];
}
}
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel {
return self.images.count;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
UIImage* img = [self.images objectAtIndex:index];
UIImageView* imgView = [[UIImageView alloc] initWithImage:img];
return imgView;
}
It's hard to be certain, but I'd guess that the first time through it's loading the images from the file; whereas the second time it's using the cached copies - that's something that +[UIImage imageNamed:] does automatically for you; without it, you'd see the performance hit every time. You could load the images earlier, either in your app delegate or in the initialiser for your controller.
Another thing you could do would be to make use of iCarousel's view reuse logic. I don't think it's responsible for your problem, but you may as well do it now:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
UIImage* img = [UIImage imageNamed:[NSString stringWithFormat:#"image%d.jpg", index]];
UIImageView* imgView = nil;
if ([view isKindOfClass:[UIImageView class]])
{
imgView = (UIImageView *)view;
imgView.image = img;
[imgView sizeToFit];
}
else
{
[[UIImageView alloc] initWithImage:img];
}
return imgView;
}
UPDATE: It would be great to get a bit more information on where your app is spending its time. If you use the Instruments Time Profiler you can get a breakdown of what percentage of the time is spent in each method.
Look at his example: Dynamic Downloads. He uses AsyncImageView instead UIImageView, that loads the images in an async way. So it won't try to load all the 15 images at the same time, and I think it shouldn't have performance issues
Can someone help me, please? I am trying to make a carousel of button - every button has a different background image, but I failed to do it.
Finally it functions, but I donĀ“t know, why I got always a warning (on
[image objectAtIndex:0], [image objectAtIndex:1], [image objectAtIndex:2]
):
'UIImage' may not respond to 'objectAtIndex'.
Thank you very much!
My source code of iCarouselExampleViewController.m:
#import "iCarouselExampleViewController.h"
#implementation iCarouselExampleViewController
#synthesize carousel;
- (void)dealloc
{
carousel.delegate = nil;
carousel.dataSource = nil;
[carousel release];
[super dealloc];
}
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
//configure carousel
carousel.type = iCarouselTypeCoverFlow2;
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.carousel = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#pragma mark -
#pragma mark iCarousel methods
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
//generate 100 buttons
//normally we'd use a backing array
//as shown in the basic iOS example
//but for this example we haven't bothered
return 5;
}
- (NSUInteger)numberOfVisibleItemsInCarousel:(iCarousel *)carousel
{
//limit the number of items views loaded concurrently (for performance reasons)
return 5;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UIButton *button = (UIButton *)view;
if (button == nil)
{
UIImage *image=[NSArray arrayWithObjects:
[UIImage imageNamed:#"image1.png"],
[UIImage imageNamed:#"image2.png"],
[UIImage imageNamed:#"image3.png"],nil];
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 200.0f, 200.0f);
[button setTitle:[NSString stringWithFormat:#"%i", index] forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
switch (index) {
case 0:
{
[button setImage:(UIImage*)[image objectAtIndex:0] forState:UIControlStateNormal];
}
break;
case 1:
{
[button setImage:(UIImage*)[image objectAtIndex:1] forState:UIControlStateNormal];
break;
}
case 2:
{
[button setImage:(UIImage*)[image objectAtIndex:2] forState:UIControlStateNormal];
}
break;
}
}
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
#pragma mark -
#pragma mark Button tap event
- (void)buttonTapped:(UIButton *)sender
{
//get item index for button
NSInteger index = [carousel indexOfItemViewOrSubview:sender];
[[[[UIAlertView alloc] initWithTitle:#"Button Tapped"
message:[NSString stringWithFormat:#"You tapped button number %i", index]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease] show];
}
#end
#end
because a UIImage isnt an NSArray
but you only DECLARED it a UIImage hence no crash
UIImage *image=[NSArray arrayWithObjects:
[UIImage imageNamed:#"image1.png"],
[UIImage imageNamed:#"image2.png"],
[UIImage imageNamed:#"image3.png"],nil];
change it so an array is declared
NSArray *image=[NSArray arrayWithObjects:...
and while at it, rename image to images and use the new syntax
NSArray *images = #[img1,img2,img3];