滚动得到了"卡住"使用嵌套滚动视图时

时间:2017-05-21 00:49:49

标签: ios objective-c scroll uiscrollview

问题描述:

我有一个iOS项目,用于浏览嵌套UIScrollViews的图片,其灵感来自着名的Apple PhotoScroller。 问题是有时滚动只是"卡住"当图像以宽度或高度方式缩放时。以下是iPhone 4s查看大小935x1400缩放高度的图片的示例:

(我开始向左拖动,但滚动视图立即丢弃此动作,图像得到"卡住")

Scroll problem

解决方法:

我通过在缩放后将内部滚动视图的内容大小调整为最接近的整数来找到一种解决方法:

// 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;
    }
}

但是这个修复并不完美 - 现在一些图像显示在侧面有一个像素宽的条纹。例如,对于尺寸为iPhone 6的图片,在690x14300上,它会在底部显示:

iPhone 6

另外,奇怪的是,我能够在iOS 7.0 - 10.1上重现此问题,但一切都在iOS 10.2及更高版本上正常运行。

问题:

那么,我做错了什么?我的修复可以改进吗?

测试项目:

我创建了一个简单的测试项目来说明所描述的问题 - NestedScrollingProblems。请注意我的ImageScrollView版本与Apple的版本略有不同,因为我应用了另一个缩放规则。此外,默认情况下,解决方法已被注释掉。 (项目代码有点乱,抱歉)

1 个答案:

答案 0 :(得分:0)

无法对帖子发表评论(还没有足够的代表)。

但从它的外观(Apple' Docs)来看,这个项目deinits会在滚动时成像,然后re-inits将它们加载(参见{{1}中的第350行}})。而且我注意到UIScrollView.m(第346行)内部的注释明确表示此类旨在避免缓存。这是一个实用的演示方式,但不适用于生产或实际应用程序,它们具有ui-loading速度,就像您想要的那样。

我还注意到你的应用程序必须进一步滚动以进行分页..这可能是代码中的一些错误,或者可能是延迟主线程流动地运行分页的延迟本身。或者如果你打算有这么宽的分页门槛......我建议减少它以获得更好的用户体验,因为现代智能手机的屏幕比iPhone 4S的屏幕宽得多。

要解决这个问题,

我在SO上发现了这个post(下图)似乎有一个相当不错的obj-c缓存方法,并在app-launch之后从这样的缓存中获取图像数据。您应该能够非常简单地将其用于启动后方法,甚至可以通过网络将其用于从Web下载图像。您只需要确保您的UIImage视图正确链接到您使用的url字符串,或者通过每个图像视图的一组自定义字符串变量,或者通过将UImageView子类化到自定义类中,并添加缓存进入它的方法,使您的代码看起来更简单。以下是来自iOSfleer

的帖子中的方法和NSCahe类

NSCache类:

ImageScrollView.m

为了不改变你所做的太多,似乎通过将ImageScrollview.m中的@interface Sample : NSObject + (Sample*)sharedInstance; // set - (void)cacheImage:(UIImage*)image forKey:(NSString*)key; // get - (UIImage*)getCachedImageForKey:(NSString*)key; @end #import "Sample.h" static Sample *sharedInstance; @interface Sample () @property (nonatomic, strong) NSCache *imageCache; @end @implementation Sample + (Sample*)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[Sample alloc] init]; }); return sharedInstance; } - (instancetype)init { self = [super init]; if (self) { self.imageCache = [[NSCache alloc] init]; } return self; } - (void)cacheImage:(UIImage*)image forKey:(NSString*)key { [self.imageCache setObject:image forKey:key]; } - (UIImage*)getCachedImageForKey:(NSString*)key { return [self.imageCache objectForKey:key]; } 方法更改为下一个(使用缓存方法),似乎在初始加载后更好地工作。如果我是你,我也会更进一步,并在控制器的displayImageWithInfo方法中实现循环风格的方法,以便立即缓存这些图像,以便在启动时加快加载速度。但这取决于你。

viewDidLoad

我还建议在没有从滚动的超级视图中删除视图的情况下解决这个问题。添加视图是一项非常繁重的任务。再加上图像加载,对于智能手机上的小型CPU来说可能会非常沉重(因为他们还没有GPU ......)。为了强调这一点,Apple甚至提到它一旦显示它就不会重新渲染UIImages,措辞很微妙here,但它显然没有提到优化删除然后在显示后重新添加和渲染视图曾经(例如在这种情况下)。我认为此处的用途是显示- (void)displayImageWithInfo:(ImageItem*)imageInfo { CGSize imageSize = (CGSize){.width = imageInfo.width, .height = imageInfo.height}; // clear the previous imageView [self.imageView removeFromSuperview]; self.imageView = nil; // reset our zoomScale to 1.0 before doing any further calculations self.zoomScale = 1.0; self.imageView = [[UIImageView alloc] initWithFrame:(CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size = imageSize}]; UIImage *image = [[Sample sharedInstance] getCachedImageForKey:imageInfo.path]; if(image) { NSLog(@"This is cached"); ((UIImageView*)self.imageView).image = image; } else{ NSURL *imageURL = [NSURL URLWithString:imageInfo.path]; UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]]; if(image) { NSLog(@"Caching ...."); [[Sample sharedInstance] cacheImage:image forKey:imageInfo.path]; ((UIImageView*)self.imageView).image = image; } } [self addSubview:self.imageView]; [self configureForImageSize:imageSize]; } ,然后在显示控制器后立即更改它的ImageView元素。

  

虽然图像对象支持所有平台原生图像格式,但它   建议您为您的大多数图像使用PNG或JPEG文件   应用程序。图像对象经过优化,可以读取和显示两者   格式,这些格式提供比大多数其他格式更好的性能   图像格式。

这就是在imageviewWillAppear之类的任何可见加载方法之前,通常在超级视图上添加/初始化视图的原因,或者如果在初始加载后完成它们很少,则初始化,他们的内容通常是唯一改变的东西,即使它通常是异步完成(如果从网上下载),或者它是从缓存完成的,也可以使用一些初始化程序自动完成(你可以添加到我的内容)我推荐):

  

使用imageNamed:inBundle:compatibleWithTraitCollection:方法(或   imageNamed:方法)从图像资产或图像创建图像   位于应用主要包中的图像文件(或其他一些已知的   束)。因为这些方法会自动缓存图像数据,   它们特别推荐用于经常使用的图像。

在个人笔记上,我会尝试采用viewDidAppear的方法。值得注意的是,当视图滚出窗口时,他们有代表自动处理内容缓存(这正是这个演示的内容)。您也可以为这些方法添加自定义代码,以便更好地控制对这些视图的滚动效果。起初他们可能有点难以理解,但我可以证明你在这里尝试完成的东西可以用这个演示使用的一小部分代码来复制。我还假设这个演示版是在2012年建立的一个提示..它是一个非常古老的演示,并且在此演示最后更新时出现了UICollectionViews。所以我说这是Apple一直以来的目标,因为所有面向内容的UIView子类都有来自UIScrollView的某种继承  (UICollectionView,UITableView,UITextView等)。值得一看! UICollectionViews

相关问题