Ant*_*gin 8 scroll objective-c uiscrollview ios
我有一个用于浏览嵌套图像的iOS项目,UIScrollViews其灵感来自着名的Apple的PhotoScroller.问题是当图像被宽度或高度缩放时,有时滚动只是"卡住".这是一个如何查看iPhone 4s尺寸935x1400缩放的图像的示例:
(我开始向左拖动,但滚动视图立即丢弃此动作,图像"卡住")
通过在缩放后将内部滚动视图的内容大小调整为最接近的整数,我找到了一种解决方法:
// Inside ImageScrollView.m
- (void)setZoomScale:(CGFloat)zoomScale
{
[super setZoomScale:zoomScale];
[self fixContentSizeForScrollingIfNecessary];
}
- (void)zoomToRect:(CGRect)rect animated:(BOOL)animated
{
[super zoomToRect:rect animated:animated];
[self fixContentSizeForScrollingIfNecessary];
}
- (void)fixContentSizeForScrollingIfNecessary
{
if (SYSTEM_VERSION_LESS_THAN(@"10.2"))
{
CGSize content = self.contentSize;
content.width = rint(content.width);
content.height = rint(content.height);
self.contentSize = content;
}
}
Run Code Online (Sandbox Code Playgroud)
但这个修复并不完美 - 现在一些图像显示在侧面有一个像素宽的条纹.例如,iPhone 6对于大小的图像,690x14300它在底部显示:
另外,奇怪的是,我能够重现这个问题iOS 7.0 - 10.1,但一切都正常,iOS 10.2并且更大.
那么,我做错了什么?我的修复可以改进吗?
我创建了简单的测试项目来说明所描述的问题 - NestedScrollingProblems.请注意我的ImageScrollView版本与Apple的版本稍有不同,因为我应用了另一个缩放规则.此外,默认情况下,解决方法已被注释掉.(项目代码有点乱,抱歉)
无法对帖子发表评论(还没有足够的代表)。
\n\n但从它的外观(Apple's Docs)来看,这个项目deinits图像是滚动的,然后是re-inits它们要加载时的图像(参见 中的第 350 行UIScrollView.m)。而且我还注意到ImageScrollView.m(第 346 行)内的一条注释明确指出该类旨在避免缓存。对于演示来说这是一种实用的方法,但对于生产或现实世界的应用程序来说则不然,因为它们需要像您想要的那样考虑用户界面加载速度。
我还注意到您的应用程序必须滚动得更远才能参与分页..这要么是代码中的一些错误,要么可能是滞后本身导致主线程无法流畅地运行分页。或者,如果您打算设置如此宽的分页阈值……我建议您减少它以获得更好的用户体验,因为现代智能手机的屏幕比 iPhone 4S 宽得多。
\n\n为了解决这个问题,
\n\n我在 SO 上发现了这篇文章(如下),它似乎有一个相当不错的 obj-c 缓存方法,并从应用程序启动后的缓存中获取图像数据。您应该能够非常简单地将其应用到启动后方法中,甚至可以将其与网络一起使用以从网络下载图像。您只需确保您的 UIImage 视图正确链接到您使用的 url 字符串,或者通过每个图像视图的一组自定义字符串变量,或者通过将 UImageView 子类化为自定义类,并添加缓存方法使您的代码看起来更简单。这是iOSfleer帖子中的方法和 NSCahe 类
\n\nNSCache 类:
\n\n@interface Sample : NSObject\n\n+ (Sample*)sharedInstance;\n\n// set\n- (void)cacheImage:(UIImage*)image forKey:(NSString*)key;\n// get\n- (UIImage*)getCachedImageForKey:(NSString*)key;\n\n@end\n\n#import "Sample.h"\n\nstatic Sample *sharedInstance;\n\n@interface Sample ()\n@property (nonatomic, strong) NSCache *imageCache;\n@end\n\n@implementation Sample\n\n+ (Sample*)sharedInstance {\n static dispatch_once_t onceToken;\n dispatch_once(&onceToken, ^{\n sharedInstance = [[Sample alloc] init];\n });\n return sharedInstance;\n}\n- (instancetype)init {\n self = [super init];\n if (self) {\n self.imageCache = [[NSCache alloc] init];\n }\n return self;\n}\n\n- (void)cacheImage:(UIImage*)image forKey:(NSString*)key {\n [self.imageCache setObject:image forKey:key];\n}\n\n- (UIImage*)getCachedImageForKey:(NSString*)key {\n return [self.imageCache objectForKey:key];\n}\nRun Code Online (Sandbox Code Playgroud)\n\n为了不改变太多你所做的事情,似乎通过将displayImageWithInfoImageScrollview.m 内部的方法更改为以下方法(使用缓存方法),它在初始加载后似乎工作得更好。如果我是你,我还会更进一步,在控制器的viewDidLoad方法中实现循环式方法,以立即缓存这些图像,以便在启动时更快地加载。但这取决于你。
- (void)displayImageWithInfo:(ImageItem*)imageInfo\n{\n CGSize imageSize = (CGSize){.width = imageInfo.width, .height = imageInfo.height};\n\n // clear the previous imageView\n [self.imageView removeFromSuperview];\n self.imageView = nil;\n\n // reset our zoomScale to 1.0 before doing any further calculations\n self.zoomScale = 1.0;\n\n self.imageView = [[UIImageView alloc] initWithFrame:(CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size = imageSize}];\n\n UIImage *image = [[Sample sharedInstance] getCachedImageForKey:imageInfo.path];\n if(image)\n {\n NSLog(@"This is cached");\n ((UIImageView*)self.imageView).image = image;\n }\n else{\n\n NSURL *imageURL = [NSURL URLWithString:imageInfo.path];\n UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]];\n\n if(image)\n {\n NSLog(@"Caching ....");\n [[Sample sharedInstance] cacheImage:image forKey:imageInfo.path];\n ((UIImageView*)self.imageView).image = image;\n }\n\n }\n\n\n [self addSubview:self.imageView];\n\n [self configureForImageSize:imageSize];\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我还建议解决这个问题,而不从滚动的超级视图中删除视图。添加视图是一项非常繁重的任务。再加上图像加载,对于智能手机等小型 CPU 来说可能会非常繁重(因为它们还没有 GPU ......)。为了强调这一点,Apple 甚至提到 UIImages 一旦显示就不会重新渲染,这里的措辞很微妙,但它显然没有提到在显示一次后优化删除然后重新添加和渲染视图(例如是在这种情况下吗)。我认为这里的预期用途是显示,并在显示控制器后ImageView简单地更改它的元素。image
\n\n\n尽管图像对象支持所有平台本机图像格式,但建议您对应用中的大多数图像使用 PNG 或 JPEG 文件。图像对象针对读取和显示这两种格式进行了优化,并且这些格式比大多数其他图像格式提供更好的性能。
\n
这就是为什么视图通常在任何可见的加载方法(如viewWillAppear和 )之前在其超级视图上添加/初始化viewDidAppear,或者如果在初始加载后完成,则它们很少被取消初始化,它们的内容通常是唯一改变的东西,即使如此它通常是异步完成的(如果从网络下载),或者是从缓存完成的,也可以使用一些初始化程序自动完成(您可以将其添加到我推荐的内容中):
\n\n\n使用 imageNamed:inBundle:兼容TraitCollection: 方法(或\n imageNamed: 方法)从位于应用程序\xe2\x80\x99s 主包(或其他一些已知\n)中的图像资源或图像文件创建图像捆)。由于这些方法会自动缓存图像数据,\n 特别推荐用于您经常使用的图像。
\n
就个人而言,我会尝试采取UICollectionViews. 值得注意的是,当视图滚动到窗口之外时,他们有代理自动处理内容缓存(这正是这个演示的内容)。您也可以向这些方法添加自定义代码,以更好地控制这些视图上的滚动效果。一开始它们可能有点难以理解,但我可以证明您在这里尝试完成的任务可以用此演示使用的一小部分代码来复制。我还认为这个演示是在 2012 年构建的这一事实作为提示。这是一个非常古老的演示,UICollectionViews 出现在该演示上次更新时。所以我想说,这就是 Apple 一直以来的目标,因为所有面向内容的 UIView 子类无论如何都具有某种来自 UIScrollView 的继承(UICollectionView、UITableView、UITextView 等)。值得一看!UICollectionViews。