我正在使用一个Objective-C应用程序,该应用程序具有一个全屏水平滚动UICollectionView的场景。我目前每次在滚动期间在屏幕上出现新单元格时都会调用一个函数,运行大约需要3-4秒,并在新出现的单元格中编辑ui元素。因此,每当新单元格进入屏幕大约4秒钟后,我的应用程序就会滞后,然后继续正常滚动。
有没有办法编辑这个功能并将其放在后台线程上,这样滚动时间不是4秒,而是无缝,不间断的滚动?我知道这个解决方案会导致ui元素出现4秒的延迟,但只要有无缝滚动,我就可以了。
编辑:我认为发布此问题的代码不是一个好主意,因为执行加载的函数中的代码需要我购买的视频播放器API,但我会继续发布下面的方法显示该功能的哪些部分编辑UI。//variables declared in other parts of the file
KolorEyes *myKolorEyesPlayer;
UICollectionView *collectionViewFollwersFeed;
NSIndexPath *lastPath = nil;
//called repeatedly while scrolling
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
if(scrollView==_collectionViewFollwersFeed)
{
[self tsLoad]; //function call
}
}
- (void)tsLoad {
//calculate index path of cell in the center of the screen
NSIndexPath *centerCellIndexPath =
[self.collectionViewFollwersFeed indexPathForItemAtPoint:
[self.view convertPoint:[self.view center] toView:self.collectionViewFollwersFeed]];
if(centerCellIndexPath != lastPath) //condition is satisfied if a new cell has been scrolled to the center
{
lastPath = centerCellIndexPath;
UICollectionViewCell *cell;
NSString *CellIdentifier;
//initialize cell from center path
CellIdentifier = @"FollowersFeed";
cell = [_collectionViewFollwersFeed cellForItemAtIndexPath:centerCellIndexPath];
//get respective url for video player
NSMutableDictionary *dict1=[follwerFeed objectAtIndex:centerCellIndexPath.row];
NSString *tsstring= [dict1 valueForKey:@"video"];
NSURL *tsurl = [[NSURL alloc] initWithString:tsstring];
//view that the video will be played in
UIView *tsView = (UIView *)[cell viewWithTag:99];
//API-specific parameters for video player session
KolorEyesSessionParams *params = [[KolorEyesSessionParams alloc] init];
params.delegate = self;
/* other params properties set like above at this point...
...
...
*/
//API-specific initialization
myKolorEyesPlayer = [[KolorEyes alloc] initWithParams:params];
//API-specific parameters for video player view
KolorEyesRenderViewParams *fparams = [[KolorEyesRenderViewParams alloc] init];
fparams.cameraFieldOfView = 90;
/* other fparams properties set like above at this point...
...
...
*/
//API-specific initializations
id _tsViewHandle = [myKolorEyesPlayer setRenderView: tsView withParams:fparams];
[myKolorEyesPlayer setCameraControlMode:kKE_CAMERA_MOTION forView:_tsViewHandle];
__block KolorEyes *player = myKolorEyesPlayer;
//error checking
eKEError err = [myKolorEyesPlayer setAssetURL: tsurl
withCompletionHandler:^{
// Media is loaded, play now
[player play];
}];
if (err != kKE_ERR_NONE) {
NSLog(@"Kolor Eyes setAssetURL failed with error");
}
}
}
答案 0 :(得分:2)
您可以为运行后台操作定义“ NSOperationQueue ”,当单元格可见时,您可以将视频加载到UICollectionView的单元格。
此外,当不再需要collectionView时,不要忘记利用NSOperationQueue并调用“ cancelAllOperations ”
答案 1 :(得分:0)
有没有办法编辑这个函数并将其放在后台线程上 因此滚动时间不是4秒,而是无缝的, 不间断的滚动?
您正在运行SDK,如上所述:
Kolor Eyes是一款免费的360视频播放器应用,可呈现球形视频 具有像素精确投影算法。用户可以选择 通过触摸手势或设备随时可以获得不同的相机视点 运动控制。它不仅仅是一个播放器,而是完美的浏览器设计 适用于Kolor 360视频托管网站。本文档涉及Kolor 从2.0版开始关注iOS
运行苹果自己的足够的问题" AVPlayer"在没有滞后的UICollectionView / UITableView中,你想要运行一个没有任何滞后的Custom made 360视频播放器。
由于它是SDK,您无法访问或修改任何代码,因此您需要与SDK的开发人员联系以获取指南。我不认为这个SDK是为了在scrollView中运行而开发的,因为这似乎是一项繁重的任务(但我不确定100%,你需要问开发人员)。
然而,你不应该修改后台线程上的用户界面,因为你要求它不会帮助延迟消失,它会让它变得更糟甚至崩溃或者在大多数情况下挂起你的应用程序,我确信(希望至少这样)开发人员已经使用这个SDK做了尽可能多的线程:
在Cocoa应用程序中,主线程运行用户界面,即 是的,所有绘图和所有事件都在主线程上处理。如果你的 应用程序对其执行任何冗长的同步操作 线程,您的用户界面可能会变得无响应并触发 旋转光标。为避免这种情况,您应该缩短时间 这些操作消耗,推迟执行或移动它们 次要线程。
答案 2 :(得分:0)
理论上你是对的。您应该能够在后台线程上进行非UI相关的视频加载(例如下载视频),并在实际想要显示时调度到主线程。在实践中,我发现很多第三方库都很难。大多数视频库都希望完全在主线程上运行。你必须检查你的视频sdk - 但如果他们支持后台加载我会感到惊讶。
另一种解决方案是不在cellForItemAtIndexPath:
中加载视频,而是在滚动停止时加载可见单元格的视频。您可以监控代理回调scrollViewDidEndDragging
和/或scrollViewDidEndDecelerating
。
答案 3 :(得分:0)
哦我之前在滚动过程中尝试过播放视频。那时候我没有回复。当你一直在谷歌搜索时,你可能也看到了my question,因为它有很多相同的逻辑?当滚动停止时我最终开始播放视频 - 但那是很久以前的事了,我不再那样了。
现在,我可能会想到你。
在你深入研究之前,我只想说:确实有效,但我不能100%确定它是否适用于视频播放。你必须自己找到它。对不起,我很抱歉,但这可能很复杂。
完全取决于你的情况,细胞数量等,你可能有兴趣禁用重复使用细胞,并简单地预编译所有东西。
之前我没有在UICollectionViewCell
完成此操作,我不知道KolorEyes
是什么,但这可能适用于UITableViewCell
和AVPlayer
,尽管我没有时间或意志测试它。
我会尝试解释我在想什么的逻辑。
我有一段时间没有做Objective-C
所以我会在Swift
做这件事,但我希望你明白我在想什么:
首先,制作这个课程:
class PrecompiledCell{
var cell:UICollectionViewCell? = nil
var size:CGSize? = nil
// constructor and such
}
这个类只是你单元格的包装器。现在,您应该为要显示的每个单元格创建一个PrecompiledCell
实例,每个关注者都需要一个I.E.
您应该创建一个名为precompileCells()
的函数,并在更新dataSource(followerFeed
)时调用它。此函数应循环遍历follwerFeed
- 数组中的所有元素,并为每个元素创建一个单元格。就像你今天cellForRowAtIndexPath
可能正在做的那样。
var followerFeed:[[String:Any]] = [] // Your data. Probably an NSArray with NSDictionaries inside?
var precompiledCells:[PrecompiledCell] = [] //Your array of precompiled cells
func precompileCells(){
var precompiledCells:[PrecompiledCell] = []
for feed in followerFeed{
//Create the cell
let videoCell = UINib(nibName: "MyVideoCell", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! MyVideoCell
//Each instance of this cell (MyVideoCell) should already be initialising its very own KolorEyes-player etc., so now you can say this:
videoCell.kolorEyesPlayer.setAssetURL(feed["video"]) //Of course, convert String to NSURL etc. first..
let precompiledCell = PrecompiledCell()
precompiledCell.cell = videoCell
precompiledCell.size = //Find out what size the cell is. E.g by using videoCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize), or even easier if you have a constant size.
precompiledCells.append(precompiledCell)
}
self.precompiledCells = precompiledCells
}
现在,两次不做这项工作很重要。这个答案的全部概念是,在使用滚动时,您的常规cellForRowAtIndexPath
(或您的情况下为scrollViewDidScroll
)不必完成所有这些工作。当前延迟的原因是视图在滚动时初始化了很多东西。使用这些预编译的单元格,在开始滚动之前,所有内容都已初始化。
所以现在,删除正常委托方法的初始化和所有出列,并执行以下操作:
func collectionView(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return self.precompiledCells[indexPath.row].cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.precompiledCells[indexPath.row].size
}
滚动时不再需要初始化内容,因为之前已经初始化了所有内容。你现在唯一需要做的就是使用你的旧函数scrollViewDidScroll
,和你做的一样(存储当前索引等),然后简单地放[player play];
,而不是其他东西。< / p>
首先,在 collectionView.reloadData()
之后调用self.precompileCells
非常重要。所以它应该是这样的:
self.followerFeed = [/*new data*/]
self.precompileCells()
self.collectionView.reloadData()
此外,据我所知,此实现将完全禁用出列,这实际上是不好的做法。如果要装入大量细胞,请不要使用此方法。然后试着把它分开一点。
此实现可能会在实际预编译时导致延迟,而不是在滚动时延迟。
如果以分页方式加载单元格(例如滚动到最后将加载更多单元格),那么您应该真正改进我的代码,因为它将重新预编译所有单元格。然后你应该制作某种insertPrecompiledCells
- thingy。
只要您预先编译了单元格,每个单元格都可以自行决定是否应该开始下载视频。如果他们应该下载整个视频,只需几秒钟的视频开始,或者在您要求播放之前不下载它。开始部分下载既可以是异步的,也可以在滚动时更加无缝地启动。
我写这篇文章的另一个想法是,为每个单元格设置KolorEyes
- 玩家可能是一个坏主意。您可能希望全局拥有一个KolorEyes
- 播放器,只需将其传递到正确的单元格 - 但您仍然可以在设置视频播放器之前预编译该单元格,您还可以开始异步下载实际的视频(没有播放器)(取决于KolorEyes播放器到底是什么,但你会想出那个部分......)
同样,这只是一个可能有用的想法。我已经将tableViews
用于复杂单元格,以大幅增加滚动的平滑度,但是以非常可控的方式(从不超过30行等)。
我仍然有很多想法和想法,但这篇文章已经太长了,我现在必须走了。祝你好运!