nicklockwood / iCarousel

A simple, highly customisable, data-driven 3D carousel for iOS and Mac OS
http://www.charcoaldesign.co.uk/source/cocoa#icarousel
Other
12k stars 2.58k forks source link

iCarousel Index on Sender starts back at 0 after 3 #522

Closed keeano92 closed 10 years ago

keeano92 commented 10 years ago

Hi,

Im working with your library and i have to say its awesome! I have been running into some hurdles recently and i think you will be bale to help me.

I am trying to play one instance of the icarousel at a time and i figure i would do that with the index being passed through my sender. Though i get this outcome

0 1 2 3 0 1 2 0 1 2

I have 10 objects,what could be causing this?

keeano92 commented 10 years ago

Still stuck on this issue, some help!!! Seriously, no one will answer my questions involving iCarousel on Stack or on this issue tracker. Your carousel object is not working appropriately and i have used it and implemented it correctly. Why does the index not stay consistent? I would like to post all of my code though i'm waiting for a response of some sort. Its been at least a week!

nicklockwood commented 10 years ago

I don't know exactly what you mean by index on sender since you haven't included any code in your question, but I'd guess that you are storing the index in your views when you first create them in carousel:viewForItemAtIndex:reusingView:, but then if the reusingView != nil you don't set the index again, so you just end up with the same three views being endlessly recycled with the same three index values.

keeano92 commented 10 years ago

I am no longer using the sender, i am using didSelectItemAtIndex though i am working with AVFoundation and would like the object at the appropriate index to [player play]; when clicked.

It seems to work though when i select item at index it plays it +1 index up, so if i click one, it plays it on index 2.....weird!

This is some code for you;

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UILabel *usernameLabel = nil;
UILabel *pointLabel = nil;
UIButton *reportUser = nil;
UILabel *postTimeLabel = nil;
UILabel *distanceLabel = nil;
BFTDataHandler *handler = [BFTDataHandler sharedInstance];

    //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
    UIView *mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 370.0f)];
    view = mainView;
    CGFloat mainViewWidth = mainView.bounds.size.width;

    //Header
    UIImageView *topTrapazoid = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, mainViewWidth, 60)];
    topTrapazoid.image = [UIImage imageNamed:@"upper-trap@2x.png"];
    topTrapazoid.tag = 4;
    [mainView addSubview:topTrapazoid];
    UILabel *responseLabel = [[UILabel alloc] initWithFrame:topTrapazoid.bounds];
    responseLabel.center = CGPointMake(180, 15);
    responseLabel.textColor = [UIColor colorWithRed:243/255.0f green:172/255.0f blue:40/255.0f alpha:1.0];
    responseLabel.font = [responseLabel.font fontWithSize:10];
    responseLabel.tag = 8;
    responseLabel.text = @"respond";
    [mainView addSubview:responseLabel];

    UIImageView *dividerTop = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 167, 1)];
    dividerTop.image = [UIImage imageNamed:@"dividerbar.png"];
    dividerTop.center = CGPointMake(100, 30);
    [mainView addSubview:dividerTop];

    //Video Player View
    _videoView = [[UIView alloc] initWithFrame:CGRectMake(0,0, mainViewWidth, 220)];
    _videoView.backgroundColor = [UIColor colorWithRed:123/255.0 green:123/255.0 blue:123/255.0 alpha:1.0];
    _videoView.center = CGPointMake(100, 170);
    UIImageView *thumbView = [[UIImageView alloc] initWithFrame:CGRectMake(_videoView.frame.origin.x, _videoView.frame.origin.y - 60
                                                                           , _videoView.frame.size.width, _videoView.frame.size.height)];
[thumbView setImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[_thumbURLS objectAtIndex:index]]]]];

    //video Thumbs

[_videoView addSubview:thumbView];
    [view addSubview:_videoView];

    //Username Display
    usernameLabel = [[UILabel alloc] initWithFrame:_videoView.bounds];
    usernameLabel.font = [usernameLabel.font fontWithSize:15];
    usernameLabel.textColor = [UIColor colorWithWhite:100 alpha:1.0];
    usernameLabel.center = CGPointMake(_videoView.center.x + 65, 270);
    usernameLabel.tag = 10;
    [mainView addSubview:usernameLabel];

    //footer
    UIImageView *bottomTrapazoid = [[UIImageView alloc] initWithFrame:CGRectMake(0, 280, mainViewWidth, 90)];
    bottomTrapazoid.image = [UIImage imageNamed:@"lower-trap@2x.png"];
    bottomTrapazoid.tag = 6;
    bottomTrapazoid.contentMode = UIViewContentModeScaleToFill;
    [view addSubview:bottomTrapazoid];
    UIImageView *dividerbtm1 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 169, 1)];
    dividerbtm1.image = [UIImage imageNamed:@"dividerbar.png"];
    dividerbtm1.center = CGPointMake(100,310);
    [mainView addSubview:dividerbtm1];
    UIImageView *dividerbtm2 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 126, 1)];
    dividerbtm2.image = [UIImage imageNamed:@"dividerbar.png"];
    dividerbtm2.center = CGPointMake(100,346);
    [mainView addSubview:dividerbtm2];
    //Labels
    UILabel *notTodayLabel = [[UILabel alloc] initWithFrame:bottomTrapazoid.bounds];
    notTodayLabel.center = CGPointMake(175, 358);
    notTodayLabel.textColor = [UIColor colorWithRed:243/255.0f green:172/255.0f blue:40/255.0f alpha:1.0];
    notTodayLabel.font = [notTodayLabel.font fontWithSize:10];
    notTodayLabel.tag = 11;
    notTodayLabel.text = @"not today";
    [mainView addSubview:notTodayLabel];

    pointLabel = [[UILabel alloc] initWithFrame:bottomTrapazoid.bounds];
    pointLabel.center = CGPointMake(235, 293);
    pointLabel.textColor = [UIColor colorWithWhite:-90 alpha:1.0];
    pointLabel.font = [pointLabel.font fontWithSize:9];
    pointLabel.tag = 12;
    [mainView addSubview:pointLabel];

    postTimeLabel = [[UILabel alloc] initWithFrame:bottomTrapazoid.bounds];
    postTimeLabel.center = CGPointMake(120, 294);
    postTimeLabel.textColor = [UIColor colorWithWhite:-100 alpha:1.0];
    postTimeLabel.font = [postTimeLabel.font fontWithSize:9];
    postTimeLabel.tag = 14;
    [mainView addSubview:postTimeLabel];

    distanceLabel = [[UILabel alloc] initWithFrame:bottomTrapazoid.bounds];
    distanceLabel.center = CGPointMake(130, 325);
    distanceLabel.textColor = [UIColor colorWithWhite:-100 alpha:1.0];
    distanceLabel.font = [distanceLabel.font fontWithSize:9];
    distanceLabel.tag = 15;
    [mainView addSubview:distanceLabel];

    reportUser = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60,10)];
    reportUser.center = CGPointMake(140, 325);
    reportUser.titleLabel.font =[reportUser.titleLabel.font fontWithSize:9];
    [reportUser setTitle:@"report user" forState:UIControlStateNormal];
    [reportUser setTitleColor:[UIColor colorWithWhite:-100 alpha:1.0] forState:UIControlStateNormal];
    [mainView addSubview:reportUser];

//Assign Item to Labels
usernameLabel.text = handler.Username[index];
pointLabel.text = @"32 points";
postTimeLabel.text = @"3 hours ago";
distanceLabel.text = @"4 miles away";

return view;
}

Right now i am using a thumb from the server to make the carousel less bogged down with data, though i would like to replace that with a AVPlayer....How can i track the object at index and get the AVPlayer to play the object at that index?

nicklockwood commented 10 years ago

I'm not sure why index would be off by one, but the carousel's item indexes are zero based maybe there's a mismatch with how you've numbered your thumbnails in your datasource?

Also, dataWithContentsOfURL is synchronous, so it's not really a good way to populate the carousel because if you're on a slow internet connection the carousel will freeze, however small the image is. Look at some of the async downloading and image examples included with the project for a better approach.

keeano92 commented 10 years ago

Yeah i understand the other methods for populating the images, right now i just need this for testing throughout my company. The data source i have is not wrong, i have checked it a million times.

Lets say you were going to implement multiple videos in iCarousel, how would you approach it? I am simply getting ten at a time and they just need to be able to view them one by one and so on.

keeano92 commented 10 years ago

my thumbs are populated correctly but when i use this code below to populate the video and play it back it plays on the wrong object

keeano92 commented 10 years ago
-(void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index {

AVPlayerItem *avPlayeritem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:[_videoURLS objectAtIndex:index]]];
AVPlayer *avPlayer = [[AVPlayer alloc] initWithPlayerItem:avPlayeritem];
AVPlayerLayer *avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:avPlayer];
[avPlayerLayer setFrame:CGRectMake(0, 0, _videoPlayback.frame.size.width, _videoPlayback.frame.size.height)];
[_videoPlayback.layer addSublayer:avPlayerLayer];
//Assign to notication to check for end of playback
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:avPlayeritem];

[avPlayer seekToTime:kCMTimeZero];
[_videoPlayback setHidden:NO];
[avPlayer play];
}
keeano92 commented 10 years ago

Sorry it should be

[_videoView.layer addSublayer:avPlayerLayer];
nicklockwood commented 10 years ago

Where is the "_videoView" ivar being set?

keeano92 commented 10 years ago

in the header file, as

@property(strong, nonatomic) UIView *videoView;
nicklockwood commented 10 years ago

No, I mean where do you set it's value? Is it always the same view, or does it change as the carousel rotates?

nicklockwood commented 10 years ago

Oh, I see. You're setting _videoView = [[UIView alloc] initWithFrame:CGRectMake(0,0, mainViewWidth, 220)]; in your viewForItemAtIndex: method. No wonder it doesn't work.

nicklockwood commented 10 years ago

_videoView is just randomly set to whichever carousel view was last loaded. You're assuming that it is the one that was just tapped, but there's no reason why it would be.

keeano92 commented 10 years ago

Why would that not work and whats the appropriate way to set it?

keeano92 commented 10 years ago

If i set it outside of the carousel it will stay in the center of the screen while the carousel rotates

keeano92 commented 10 years ago

I get what your saying now, but how can i handle it more appropriately?

nicklockwood commented 10 years ago

Carousel views aren't loaded when you tap them. They are loaded when the carousel scrolls, and the carousel doesn't neccesarily load the currently centered item each time it moves, even if there's only one item visible on screen.

Update the _videoView in the - (void)carouselCurrentItemIndexDidChange:(iCarousel *)carousel; delegate method instead.

nicklockwood commented 10 years ago

From inside that method, you can get the currently centered index using carousel.currentItemIndex and get a reference to the current carousel view using carousel.currentItemView.

keeano92 commented 10 years ago

when would i handle the playback, when item at index is selected or in did change? Could you explain what did change does in more detail, i'm a little confused as to why i would have to iterate my view there because i need it for all 10 objects.

nicklockwood commented 10 years ago

Do you want videos to play when the user taps them, or when they rotate the carousel?

keeano92 commented 10 years ago

when they tap the image, i was going to do a button but i removed it in my recent code once i figured out how to get the item selected at index

nicklockwood commented 10 years ago

ok, then you want to play the video when didSelectItemAtIndex: is pressed, but you probably want to update the _videoView when the carousel index changes (in carouselCurrentItemIndexDidChange) so that it's showing the correct thumbnail.

You also probably don't want to keep adding new views to the _videoView because it will get slower and slower over time. Maybe just have one imageView inside the _videoView permanently, and just update it's image property with the new thumbnail each time carouselCurrentItemIndexDidChange happens.

keeano92 commented 10 years ago

what if i was to take out the thumb nails and just use an AVPlayer? How should i handle that so that i can play the object at the index and the views are never re-used.

keeano92 commented 10 years ago

I was able to get it working beautifully now, i was curious if you know why the video layer would be adding space on the top and bottom instead if filling the available space?

nicklockwood commented 10 years ago

No idea, sorry.