我必须在我的应用中显示一个巨大的文本文件。
UITextView
不符合我的要求,因为它强制换行,所以我不得不使用UILabel
。由于非常大的标签不会被渲染,我在UILabels
内使用了多个UIScrollView
来使其正常工作。
一切都在模拟器上运行,但UILabels
所需的内存大约为300MB。当我在iPad 2上运行时,内存不足,应用程序崩溃。
问题在于我没有得到任何内存警告。我想解除didReceiveMemoryWarning
中的视图控制器,但它没有被调用,应用程序崩溃而没有任何警告。
我错过了什么?
答案 0 :(得分:1)
以下是使用UITableView
解决问题的示例。
<强> HSViewController.h 强>
@interface HSViewController : UITableViewController
@end
<强> HSViewController.m 强>
#import "HSViewController.h"
//#define USE_LABEL
static NSString *const kCellIdentifier = @"kCellIdentifier";
@interface HSViewController ()
@property (atomic, strong) NSArray *linesOfText;
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSLineBreakMode lineBreakMode;
- (CGSize)sizeForString:(NSString *)text;
@end
@implementation HSViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.font = [UIFont systemFontOfSize:14.0f];
self.lineBreakMode = NSLineBreakByWordWrapping;
[self.tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:kCellIdentifier];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *draculaData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"Dracula" withExtension:@"txt"]];
NSString *text = [[NSString alloc] initWithData:draculaData encoding:NSASCIIStringEncoding];
#ifndef USE_LABEL
self.linesOfText = [text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
#endif
dispatch_async(dispatch_get_main_queue(), ^{
#ifdef USE_LABEL
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f,
self.view.bounds.size.width, self.view.bounds.size.height)];
label.font = self.font;
label.lineBreakMode = self.lineBreakMode;
label.numberOfLines = 0;
label.text = text;
[self.view addSubview:label];
#else
NSLog(@"Starting reloading %lu rows", (unsigned long)[self.linesOfText count]);
[self.tableView reloadData];
NSLog(@"Reload finished");
#endif
});
});
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#ifdef USE_LABEL
return 0;
#else
return [self.linesOfText count];
#endif
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
@autoreleasepool {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath];
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = self.font;
cell.textLabel.lineBreakMode = self.lineBreakMode;
cell.textLabel.text = self.linesOfText[indexPath.row];
cell.userInteractionEnabled = NO;
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
@autoreleasepool {
NSString *text = self.linesOfText[indexPath.row];
// Don't let the line height be 0
if ([text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0)
{
text = @"A";
}
return ceil([self sizeForString:text].height);
}
}
#pragma mark - Private
- (CGSize)sizeForString:(NSString *)text
{
@autoreleasepool {
if ([text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])
{
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
style.lineBreakMode = self.lineBreakMode;
return [text boundingRectWithSize:CGSizeMake(self.tableView.bounds.size.width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName : self.font,
NSParagraphStyleAttributeName : [style copy]}
context:[NSStringDrawingContext new]].size;
}
else
{
return [text sizeWithFont:self.font
constrainedToSize:CGSizeMake(self.tableView.bounds.size.width, CGFLOAT_MAX)
lineBreakMode:self.lineBreakMode];
}
}
}
@end
您会发现内存使用率实际上没有太大差异。 UITableView
版本仅保存约。内存占10%。
但是,这是动态加载表格视图单元格的起点。因此,当用户向下滚动(让屏幕显示为90%)时,可以通过更改tableView:numberOfRowsInSection:
的返回值来加载下一个X单元格。