YTPlayerView inside UITableViewCell - ios

I have a YTPlayerView inside of my subclass of UITableViewCell. In my UIViewController I call [cell.youtubeView loadWithVideoId:f.videoID]; from my tableViewDelagate willDisplayCell method. The problem is that when I have many cells in my tableView some of them stay white instead of the YouTube content!
Youtube API recommends when reusing the YTPlayerView to load the content by calling
[cell.youtubeView cueVideoById:f.videoID startSeconds:0 suggestedQuality:kYTPlaybackQualityHighRes]; instead. Unfortunately, this method doesn't load YouTube content at all.
Anybody came across the same issue? Any known solution?
I was thinking to load first time the content with loadWithVideoId and then cueVideoById but that didn't work.

I had this problem using YTPlayerView instances in UIColectionViewCell instances. Ultimately what I had to do was stop the video in didEndDisplayingCell ([self.playerView stopVideo]). Then in prepareForReuse (or optionally in cellForRowAtIndexPath), nil out your playerView, and in cellForRowAtIndexPath re-init the player and load the video by id.

I used YTPlayerView in UICollectionView(Similar to your case).
In my case, YTPlayerView got black when it was drawn in reused UICollectionViewCell(load properly on firstly initiated cell).
YTPlayerView got black is because -loadWithVideoId:playerVars: is called twice before the firstly invoked method is complete.
I checked white YTPlayerView is for not loaded video, so I recommend checking -loadWithVideoId: or similar methods called properly in somewhere.

Swift 3
I had the same issue. My problem was that the method load(withVideoId: videoId) was called several times (because of the cellForRow called several times while scrolling).
I solved this by creating a Boolean and making sure the load(withVideoId: videoId) is called only once :
1) In the cell class, create a global Boolean
var isAlreadyPlaying : Bool = false
2) When you want to play your video, we set our boolean to avoid playing it several times :
func loadVideo() {
//Making sure we are not playing the video several time
if isAlreadyPlaying == false {
//Creating vars (optional)
let playerVars = ["playsinline": 1, "autoplay": 1, "autohide": 1, "controls" : 1, "showinfo" : 0, "modestbranding" : 1, "rel" : 0]
// Launching the video
youtTubeView?.load(withVideoId: videoId, playerVars : playerVars)
// Force to not play the video again
isAlreadyPlaying = true
}
}

Here is another approach, purists won't like it, but I couldn't find out how to properly free/re-init the YTPlayerView created in my custom UICollectionViewCell.
Considering you will only play one video at time, because two several instances of YTPlayer won't play simultaneously, you just have to hide reused cells with video's thumbnail. Simply put an UIImageView above the YTplayerView in your sublclass of UICollectionViewCell xib, and set the thumbnail in it:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
//... load customcell xib, manage cellId etc.
// set video id that will be read by the custom cell
[cell setVideoId:cellVideoId];
// set above UIImage
NSData * thumbnailData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: videoThumbnailURL]];
UIImage * videoThumbnail = [UIImage imageWithData: thumbnailData];
[cell.thumbnailImage setImage:videoThumbnail];
return cell;
}
There, in custom UICollectionViewCell .m file you just need to implement a button that launch the video when clicked ...
- (IBAction)clickPlay:(id)sender {
if (videoId != nil){
[ccellYTPlayerView loadWithVideoId:videoId playerVars:playerVars];
// Hide thumbnail image to reveal the YTPlayerView
[thumbnailImage setHidden:true];
}
}
... and configure reusability of this custom cell, which works flawlessly for native obvjects :
-(void) prepareForReuse{
[super prepareForReuse];
// preparing UIImage to host another thumbnail
thumbnailImage.image = nil;
[thumbnailImage setHidden:false];
}
This way, YTplayers can appear in other cells, no one will see it because hidden by the thumbnail, and the sound produced is not a problem because user will assume that it comes from the original cell (which is not on screen anymore because of scrolling).
I know that's not really clean, but it helped me so far, YTPlayer instance is not so easy to recycle.

Related

How to refresh individual UITableViewCells?

I'm having UITableView with few UITableViewCells,
Which plays Different Audio files based on their index path.
I'm showing play/pause button in UITableViewCell.
on play button click in a UITableViewCell,i wanted to reset play button state for last played cell to default(Play).
Storing index of cell in which the play button tapped would do the thing,but we need to reload all the UITableView,Instead of reloading entire UITableView,can't we refresh/reload individual UITableViewCells?
Any help will be appreciated,
Thanks in-advance.
Two step:
1.find get the press row by rewrite your UITableCellView's button's action method
2.update the press row's image
Abstract tip: When using table views have every case implemented in its cellForRow method, that is in its data source. After any change of state or actual data just reload the table with reloadData.
Work for me every time.
More detailed: In your case - have a global index var to show which row is 'playing' now. In cellForRow check if current and global indexes match and show the right button.
Note, that you can also assign tag to any view (buttons also), this can be useful to know which row each button belongs to.
if you have IndexPath of selected cell; then you can get the cell by
UITableViewCell *cell = [UITableviewObj cellForRowAtIndexPath:selIndexPath];
now you have reference to cell; you can change cells content. i would suggest not to change height of the cell through this process.
If I understand correctly, when a user taps a Play button in a different cell, you want to pause the one currently playing and play the new one. The previous cell image should go back from Pause to Play.
In your code where you change Play to Pause and start playing, save an Index path of that cell. Next time Play is clicked check if you have an Index path saved, get the cell and reset the image on it. Something like this:
tableView:didSelectCell {
if (lastPlayingCellPath) {
MyCell *cell = [tableView cellForRowAtIndexPath:lastPlayingCellPath];
[cell setPlayImage];
}
// Play Audio and change current cell image to Pause
lastPlayingCellPath= selectedCellPath;
}
The above is pseudo code, but you should get the idea.
EDIT: From the comments I figured that your custom UITableViewCell subclass handles the tap event. You could fire a notification that you are starting to play. Every cell would subscribe to that event and reset the state of it's button. So you could do something like this in your subclass
- (instancetype)init {
// standard init code
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willPlay) name:#"CellWillPlayNotification" object:nil];
}
- (void)willPlay {
[self setPlayImage];
}
-(void)buttonTapped {
[[NSNotificationCenter defaultCenter] postNotificationName:#"CellWillPlayNotification" object:self];
// Do your standard handling of the tap
}

Two clickable items in UITableView

I have a UITableview as a contact list in which there are a lot of users. It has a thumbnail photo and profile details on each row. I want to make it like when clicking on thumbnail, it goes another page for photo and when clicking on the rest of the space it goes to somewhere else. By using table view delegate I know which row is clicked and pass data, like user id to a new ViewController. But can I know which row when the thumbnail is clicked?
I am using the tag to find the view from cell, like
UIImageView *thumbnailView = (UIImageView *) [cell viewWithTag:1];
I think I cannot label the row index by tag.
You can add gesture to thumbnail image and get events on it. Need to set tag for thumb image as per indexPath.row.
Add following code in you in cell datasource method (cellForRowIndexPath:) :
cell.YOURIMGVIEW.userInteractionEnabled = YES;
cell.YOURIMGVIEW.tag = indexPath.row;
UITapGestureRecognizer *clickable = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageClicked:)];
clickable.numberOfTapsRequired = 1;
[cell.YOURIMGVIEW addGestureRecognizer:clickable];
[clickable release];
And also used below method :
-(void)imageClicked:(id)sender
{
UITapGestureRecognizer *gesture = (UITapGestureRecognizer *) sender;
NSLog(#"image tag is = %d", gesture.view.tag);
////////
You custome come goes Here !!!!!!!!!!!!!!
///////
}
You can use the photo as accessory view. Or use a UIButton with the photograph as background and set the action accordingly. (e.g. call a method on the view controller which then performs the push or segue. Doing so you will have to pass some data to the view controller indicating which row was actually clicked in. The number of the row can be used or you can set the tag of the button with some numeric id.)
Go the Easy way because doing it hard-way won't grant you a president award, right ?
Instead of UIImageView take a UIButton and set the Image on UIButton instance.
Set Button tag as the indexPath.row so that when you retrieve it you know which row is clicked. You can also sort it the other way but it seems quiet handy.
Add target into the button to a custom function. [ btnObj addTarget ...... ]
tyepcast you sender to a UIButton and receive the tag (indexpath.row)
You can remove all gesture recognizers and buttons in tableviewcell, and in your controller, you can implement below delegate;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
To make this method triggered, you need to set tableview delegate as your controller, either via code as below, or using storyboard.
- (void)viewDidLoad {
self.tableview.delegate = self;
}
By doing this, it won't matter which item you clicked in your cell, delegate method will be called always.
I hope this helps.

Update a UIView when UITableViewCell is selected

I have an application that has a UITableView and a custom UIView on the same storyboard. The UIView loads a custom chart based upon the selection, and is loading a UIWebView with HighChart (the data and html is all locally stored, not being pulled over the network).
When I select the UITableViewCell, the view never displays. It loads the subviews (I'm NSLoging that out to console) and all the data is correct, but the display itself never refreshes. I've tried sticking [myCustomView setNeedsDisplay] at 20+ different places, but I'm starting to feel like it's something else.
Is it possible that there is an issue because I'm loading a web view? Is it essentially redrawing my UIView before the asynchronous call to my UIWebView is ready to do anything? What's the easiest way to test this?
Updated with some code (Skipping some, I'll fill in more as requested):
#implemention MainViewController
-(void)viewDidLoad {
***normal viewDidLoad stuff***
_myCustomView = [[MyCustomViewClass alloc] initWithFrame: CGRectMake(0,_tableview.frame.origin.y + _tableview.frame.size.height,_tableview.frame.size.width, 176)];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomJsonDataObject *dataObject = [self.jsonDataArray objectAtIndex:[indexPath row]];
[_myCustomView redrawWithDataObject:dataObject];
}
#end
#implementation myCustomViewClass {
UIScrollView *scrollView
}
-(void)redrawWithDataObject:(MyCustomJsonDataObject *)dataObj {
scrollView = [[UIScrollView alloc] initWithFrame:etc];
// THere are 3 screens initialized: 1 chart data with a UIWebView, and two custom UIViews.
scrollView.contentSize = CGSizeMake(scrollView.size.width * 3, 176)
** initialize web view from custom class **
[scrollView addSubview:webView];
** initialize custom view from custom class **
[scrollView addSubview:UIView1];
** initialize custom view from custom class **
[scrollView addSubview:UIView2];
** set some scrollView properties (scrollable, paging, etc) **
[self addSubview:scrollView];
[self setNeedsDisplay];
}
The custom UIWebView class takes data from the JSON object, and replaces javascript placeholder variables in a local html file, then feeds the new file back into the webView. The others two views are just a bunch of simple labels that populate data from the custom data object.
I figured out my issue:
_myCustomView was already being defined in the storyboard, so it was initializing itself. When I called the init method in my viewDidLoad, it appears I was adding a SECOND instance of the view, overwriting the first, which is why it displayed blank and didn't appear to refresh. When I removed the init method for the view in viewDidLoad, it worked without issue.

MPMoviePlayerViewController in a custom cell

I have an app with a news feed screen, like the one in the facebook app. For this, I have made a custom cell, which has a contentView which is a UIView. This contentView gets initialized for displaying text, an image or video. This is done in the class for the custom cell. For the video, I'm trying to add MPMoviePlayerViewController as a subview to the cell's contentView:
MPMoviePlayerViewController *player = [[MPMoviePlayerViewController alloc] initWithContentURL:nil];
player.view.frame = self.contentView.frame;
player.view.tag = kVideoView;
[self.contentView addSubview:player.moviePlayer.view];
Then, when I load that cell in the tableviewcontroller, I want to give it contentURL, like this:
MPMoviePlayerViewController *controller = (MPMoviePlayerViewController *)[cell viewWithTag:kVideoView];
controller.moviePlayer.contentURL = [NSURL URLWithString:postInfo.uri];
return;
This crashes the app with an error:
-[MPMovieView moviePlayer]: unrecognized selector sent to instance
How do I fix this?
As you are adding player.moviePlayer.view to content view.
So while retrieving you will get reference to player.moviePlayer.view only not MPMoviePlayerViewController.
My suggestion is don't create multiple MPMoviePlayerViewController instance. You can add some UIButton or UIImageView on cell with snapshot of particular video. And while playing only you can create MPMoviePlayerViewController instance and play your video

UICollectionView reloadData ghost cells

I came here because I have a pretty weird problem with my UICollectionView.
Imgur album with all the screens I would like to share: http://imgur.com/a/v3sox
First of all, here's what my app looks like with no problem: Image 2 of the album.
Notice the refresh button in the top-right corner, this is the one which is giving me problems.
When I scroll down the UICollectionView very fast, and press "refresh" while the Collection View is still scrolling, I've got "cell leftovers" which do not belong to anybody and just stay on the screen.
(ex: Image 1 of the album)
I don't understand what's happening because in my refresh method I have:
- (void)refreshData
{
//A bunch of code before I reset the data
dispatch_async(dispatch_get_main_queue(), ^{
//Reset data
currentProjects = nil;
[self.projectsCollectionView reloadData];
//Some other stuff concerning the little "Chargement..." view
};
}
The cells then proceed to stay on my screen on every folder, so if I choose another folder, I'll get Image 3 of the album (notice the labels for the last 4 cells, they are mixed up, because the previous cells are still on the screen when they shouldn't be.)
Has anybody got an idea on what could possibly cause this? Thanks a lot for your time
P.S: sorry i put all the screens in an album, couldn't post more than 2 links so I had to improvise
Just had the same problem too. I solved it by modifying the UICollectionViewCell.
(note: my code is c# using MonoTouch).
before the cell was creating the objects each time. ie.
textView = new UITextField()
{
BackgroundColor = UIColor.Clear,
TextColor = UIColor.LightGray,
Text = Title
};
to fix it, check for the object, and update it accordingly.
if (textView == null)
{
textView = new UITextField()
{
BackgroundColor = UIColor.Clear,
TextColor = UIColor.LightGray,
Text = Title
};
}
else
textView.Text = Title;
now when reload data is called on the CollectionView, it reloads correctly.
I have had this similar issue once. The problem is my custom cell creates subview again and again.
From my guess, you should change the value of the label instead re-create it after dequeue.

Resources