cli*_*bon 12 iphone performance objective-c uiscrollview
我正在努力编写应用程序的部分应该像本机iphone照片应用程序.看看Orielly的iphone sdk app开发书,它提供了一个实现这个所谓的页面轻弹的示例代码.那里的代码首先创建所有子视图,然后隐藏/取消隐藏它们.在给定时间,只有3个子视图可见隐藏.经过努力,我得到了应用程序,当时只有大约15页.
一旦我添加了300页,就会发现预先分配这么多子视图的方法存在性能/内存问题.然后我想可能是我的情况我应该只分配3个子视图而不是隐藏/取消隐藏它们.可能是我应该在运行时删除/添加子视图.但无法弄清楚UIScrollView是否可以动态更新内容.例如,在开始时,如UIScrollView所理解的,在屏幕上的不同x偏移(0,320,640)处有3个帧.一旦用户移动到第3页,我如何确保我能够添加第4页并删除第1页但是UIScrollView不会混淆?
希望有这种问题的标准解决方案......有人可以指导吗?
Adr*_*ski 15
按照所说的,你可以只使用有限数量的资源来展示数千个元素(是的,它确实是一个Flyweight模式).这里有一些代码可以帮助你做你想做的事情.
UntitledViewController类只包含一个UIScroll并将自己设置为其委托.我们有一个NSArray,里面有NSString实例作为数据模型(其中可能有数千个NSStrings),我们希望使用水平滚动在UILabel中显示每个.当用户滚动时,我们将UILabels移动到左侧,另一个在右侧,这样一切都准备好进行下一个滚动事件.
这是界面,相当简单:
@interface UntitledViewController : UIViewController <UIScrollViewDelegate>
{
@private
UIScrollView *_scrollView;
NSArray *_objects;
UILabel *_detailLabel1;
UILabel *_detailLabel2;
UILabel *_detailLabel3;
}
@end
Run Code Online (Sandbox Code Playgroud)
这是该类的实现:
@interface UntitledViewController ()
- (void)replaceHiddenLabels;
- (void)displayLabelsAroundIndex:(NSInteger)index;
@end
@implementation UntitledViewController
- (void)dealloc
{
[_objects release];
[_scrollView release];
[_detailLabel1 release];
[_detailLabel2 release];
[_detailLabel3 release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_objects = [[NSArray alloc] initWithObjects:@"first", @"second", @"third",
@"fourth", @"fifth", @"sixth", @"seventh", @"eight", @"ninth", @"tenth", nil];
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 460.0)];
_scrollView.contentSize = CGSizeMake(320.0 * [_objects count], 460.0);
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = YES;
_scrollView.alwaysBounceHorizontal = YES;
_scrollView.alwaysBounceVertical = NO;
_scrollView.pagingEnabled = YES;
_scrollView.delegate = self;
_detailLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 460.0)];
_detailLabel1.textAlignment = UITextAlignmentCenter;
_detailLabel1.font = [UIFont boldSystemFontOfSize:30.0];
_detailLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(320.0, 0.0, 320.0, 460.0)];
_detailLabel2.textAlignment = UITextAlignmentCenter;
_detailLabel2.font = [UIFont boldSystemFontOfSize:30.0];
_detailLabel3 = [[UILabel alloc] initWithFrame:CGRectMake(640.0, 0.0, 320.0, 460.0)];
_detailLabel3.textAlignment = UITextAlignmentCenter;
_detailLabel3.font = [UIFont boldSystemFontOfSize:30.0];
// We are going to show all the contents of the _objects array
// using only these three UILabel instances, making them jump
// right and left, replacing them as required:
[_scrollView addSubview:_detailLabel1];
[_scrollView addSubview:_detailLabel2];
[_scrollView addSubview:_detailLabel3];
[self.view addSubview:_scrollView];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[_scrollView flashScrollIndicators];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self displayLabelsAroundIndex:0];
}
- (void)didReceiveMemoryWarning
{
// Here you could release the data source, but make sure
// you rebuild it in a lazy-loading way as soon as you need it again...
[super didReceiveMemoryWarning];
}
#pragma mark -
#pragma mark UIScrollViewDelegate methods
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// Do some initialization here, before the scroll view starts moving!
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self replaceHiddenLabels];
}
- (void)displayLabelsAroundIndex:(NSInteger)index
{
NSInteger count = [_objects count];
if (index >= 0 && index < count)
{
NSString *text = [_objects objectAtIndex:index];
_detailLabel1.frame = CGRectMake(320.0 * index, 0.0, 320.0, 460.0);
_detailLabel1.text = text;
[_scrollView scrollRectToVisible:CGRectMake(320.0 * index, 0.0, 320.0, 460.0) animated:NO];
if (index < (count - 1))
{
text = [_objects objectAtIndex:(index + 1)];
_detailLabel2.frame = CGRectMake(320.0 * (index + 1), 0.0, 320.0, 460.0);
_detailLabel2.text = text;
}
if (index > 0)
{
text = [_objects objectAtIndex:(index - 1)];
_detailLabel3.frame = CGRectMake(320.0 * (index - 1), 0.0, 320.0, 460.0);
_detailLabel3.text = text;
}
}
}
- (void)replaceHiddenLabels
{
static const double pageWidth = 320.0;
NSInteger currentIndex = ((_scrollView.contentOffset.x - pageWidth) / pageWidth) + 1;
UILabel *currentLabel = nil;
UILabel *previousLabel = nil;
UILabel *nextLabel = nil;
if (CGRectContainsPoint(_detailLabel1.frame, _scrollView.contentOffset))
{
currentLabel = _detailLabel1;
previousLabel = _detailLabel2;
nextLabel = _detailLabel3;
}
else if (CGRectContainsPoint(_detailLabel2.frame, _scrollView.contentOffset))
{
currentLabel = _detailLabel2;
previousLabel = _detailLabel1;
nextLabel = _detailLabel3;
}
else
{
currentLabel = _detailLabel3;
previousLabel = _detailLabel1;
nextLabel = _detailLabel2;
}
currentLabel.frame = CGRectMake(320.0 * currentIndex, 0.0, 320.0, 460.0);
currentLabel.text = [_objects objectAtIndex:currentIndex];
// Now move the other ones around
// and set them ready for the next scroll
if (currentIndex < [_objects count] - 1)
{
nextLabel.frame = CGRectMake(320.0 * (currentIndex + 1), 0.0, 320.0, 460.0);
nextLabel.text = [_objects objectAtIndex:(currentIndex + 1)];
}
if (currentIndex >= 1)
{
previousLabel.frame = CGRectMake(320.0 * (currentIndex - 1), 0.0, 320.0, 460.0);
previousLabel.text = [_objects objectAtIndex:(currentIndex - 1)];
}
}
@end
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助!
UIScrollView只是UIView的子类,因此可以在运行时添加和删除子视图.假设您有固定宽度的照片(320 300 * 320像素)并且有300张,那么您的主视图将是像素宽.创建滚动视图时,将框架初始化为该宽度.
因此滚动视图的框架将具有尺寸(0,0)到(96000,480).无论何时添加子视图,都必须更改其框架,使其适合父视图中的正确位置.
所以我们假设我们将第4张照片添加到滚动视图中.它的帧是从(960,480)到(1280,480).如果你能以某种方式将索引与每张图片相关联,这很容易计算.然后使用它来计算索引从0开始的图片帧:
Top-Left -- (320 * (index - 1), 0)
Run Code Online (Sandbox Code Playgroud)
至
Bottom-Right -- (320 * index, 480)
Run Code Online (Sandbox Code Playgroud)
删除第一张图片/子视图应该很容易.保持当前屏幕上的3个子视图的数组.每当您向屏幕添加新的子视图时,也将其添加到此数组的末尾,然后从屏幕中删除此数组中的第一个子视图.
非常感谢Adrian提供的非常简单和强大的代码示例.这段代码只有一个问题:当用户进行"双滚动"时(IE当他没有等待动画停止并一次又一次地重新滚动滚动视图时).
在这种情况下,只有在调用"scrollViewDidEndDecelerating"方法时,3个子视图位置的刷新才有效,结果是在屏幕上显示子视图之前的延迟.
通过添加几行代码可以轻松避免这种情况:
在界面中,只需添加:
int refPage, currentPage;
Run Code Online (Sandbox Code Playgroud)
在实现中,在"viewDidLoad"方法中初始化refPage和currentPage,如下所示:
refpage = 0;
curentPage = 0;
Run Code Online (Sandbox Code Playgroud)
在实现中,只需添加"scrollViewDidScroll"方法,如下所示:
- (void)scrollViewDidScroll:(UIScrollView *)sender{
int currentPosition = floor(_scrollView.contentOffset.x);
currentPage = MAX(0,floor(currentPosition / 340));
//340 is the width of the scrollview...
if(currentPage != refPage) {
refPage = currentPage;
[self replaceHiddenLabels];
}
}
Run Code Online (Sandbox Code Playgroud)
etvoilà!
现在,即使用户从未停止动画并且从不调用"scrollViewDidEndDecelerating"方法,也会在正确的位置正确替换子视图!
| 归档时间: |
|
| 查看次数: |
12403 次 |
| 最近记录: |