原文地址:http://www.appcoda.com/uiscrollview-introduction/
2015年6月17日作者:Joyce echessa
在iOS开发中,滚动视图(UIScrollView)通常用于视图内容和屏幕尺寸不一样的情况下。滚动视图有两个一下注意作用:
>用户可以通过拖拽来显示他们想看到的内容
>用户可以通过捏合手势来缩放内容
iOS应用中常用的表格视图(UITableView)就继承自滚动视图,并且提供了一个很好的方法去显示比屏幕大很多的内容。
在本教程中,我们将讨论滚动视图的诸多方面,主要包括使用纯代码和可视化编程两种方式来创建一个滚动视图、实现滚动和缩放功能,以及如何嵌套使用滚动视图。
继续阅读之前,首先要下载项目需要的资源文件,本项目中需要使用。
以代码方式创建滚动视图
UIScrollView的创建同其他视图一样,可以通过纯代码或者可视化编程两种方式来创建。在创建之后,只需要少量额外的配置就可以让UIScrollView获得基本的滚动功能。
UIScrollView和其他视图一样,被创建后应该添加到一个控制器或者添加到某个视图层级中。想要完成滚动功能还需要对UIScrollView进行以下两步设置:
>你必须设置UIScrollView的contentSize属性,也就是可滚动内容的大小。它指定了可滚动的区域的大小。
>你必须为UIScrollView添加一个或多个用于显示和滚动的子视图,这些视图提供了UIScrollView显示的内容。
你还可以根据应用的具体需求设置UIScrollView的一些显示效果,比如:是否显示水平和竖直方向的滚动条、滚动的弹性效果、缩放的弹性效果,以及允许的滚动方向等。
接下来我们将在代码中创建一个UIScrollView。在下载的资源文件中打开ScrollViewDemo工程。它就是一个简单的Single View Application工程,只不过将storyboard中根控制器的类型绑定为自己新建的叫做ScrollViewController的控制器,还在项目中添加了一张我们要用到的图片,图片名称为image.png。
var scrollView: UIScrollView! var imageView: UIImageView!
修改viewDidLoad()方法如下:
override func viewDidLoad() { super.viewDidLoad() imageView = UIImageView(image: UIImage(named: "image.png")) scrollView = UIScrollView(frame: view.bounds) scrollView.backgroundColor = UIColor.blackColor() scrollView.contentSize = imageView.bounds.size scrollView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight scrollView.addSubview(imageView) view.addSubview(scrollView) }
当你启动应用后,你会发现图片初始显示区域是它左上角的部分。
这是因为滚动视图的bounds的起点默认为(0, 0),代表了左上角。如果你想改变启动后显示的位置,你需要更改滚动视图的bounds的起点。因为这种需求经常被提起,所以UIScrollView专门提供了一个属性contentOffset用来实现这种需求。
scrollView.contentOffset = CGPoint(x: 1000, y: 450)
缩放
我们已经添加了一个UIScrollView,并且能够让用户通过拖拽来观看尺寸大于屏幕尺寸的内容。相当棒,但如果视图能够缩放的话会带来更好的体验。
要支持缩放功能,你必须为UIScrollView设置一个代理,而且代理必须遵守UIScrollViewDelegate协议,代理还需要实现viewForZoomingInScrollView()方法,该方法返回想要被缩放的视图。
你还应该为缩放设置一个比例,可以通过UIScrollView的minimumZoomScale和maximumZoomScale这两个属性来实现,它们的默认值都是1.0。
class ScrollViewController: UIViewController, UIScrollViewDelegate {
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { return imageView }
scrollView.delegate = self scrollView.minimumZoomScale = 0.1 scrollView.maximumZoomScale = 4.0 scrollView.zoomScale = 1.0
从上面的图片中我们可以发现,我们之前将minimumZoomScale设置为0.1实在是太小了,屏幕空出了很多空闲的地方。在横屏模式下,空闲的区域看上去更大。我们希望图片能在某一方向上能与屏幕相匹配,让图片既能完全显示,又能尽量减少屏幕的空闲空间。
要达到这样的效果,你必须通过图片尺寸和UIScrollView的尺寸来计算最小的缩放比例。
scrollView.minimumZoomScale = 0.1 scrollView.maximumZoomScale = 4.0 scrollView.zoomScale = 1.0
在控制器类中添加如下方法。在方法中,我们算出图片同UIScrollView的高度和宽度的比值,并将最小缩放比例设置为两者中更小的那个。注意,我们已经删除了maximumZoomScale的设置,所以它的默认值为1.0。
func setZoomScale() { let imageViewSize = imageView.bounds.size let scrollViewSize = scrollView.bounds.size let widthScale = scrollViewSize.width / imageViewSize.width let heightScale = scrollViewSize.height / imageViewSize.height scrollView.minimumZoomScale = min(widthScale, heightScale) scrollView.zoomScale = 1.0 }
setZoomScale()
在viewWillLayoutSubviews()方法中也需要调用该方法,这样当用户改变屏幕方向后,图片的尺寸仍然是正确的。
override func viewWillLayoutSubviews() { setZoomScale() }
从上面的图片中我们可以发现,图片是被定位在屏幕左上角的,我们希望将它放在屏幕中间。
向这个类添加下面的方法:
func scrollViewDidZoom(scrollView: UIScrollView) { let imageViewSize = imageView.frame.size let scrollViewSize = scrollView.bounds.size let verticalPadding = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 0 let horizontalPadding = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 0 scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding) }
运行程序,你会发现当你缩小图片时,图片始终保持在屏幕的中间。
通过双击缩放
UIScrollView默认只支持通过捏合手势来实现缩放效果,如果想实现通过双击来缩放,则需要自己做些额外的设置。
iOS人机界面指南中介绍了可以通过双击手势来达到缩放的效果。使用双击手势进行缩放需要一定的前提:要缩放的视图只能在最大和最小比例两个固定值之间来回缩放,就像苹果官方的相册应用一样,当你双击图片时,图片放大至最大,当你再次双击时,图片缩小至最小,或者可以通过连续的双击使视图一点点达到最大,然后再次双击的时候,将视图恢复为全屏显示。但是大多数应用需要实现更灵活的双击缩放效果,例如地图应用,当你双击时会使其放大,继续双击会继续放大,想要缩小则可以使用双指捏合手势来实现。
为了在你的应用程序中实现点击缩放功能,你必须在类中实现触摸处理,也就是UIScrollView的代理方法viewForZoomingInScrollView()。这个类将负责监听屏幕上手指的数量和点击的次数。当他发现是单机、双击或者两个手指触摸时,它将做出相应的反应。再双击和两根手指触摸的情况下,它将进行适当的缩放处理。
在我们的应用中,如果视图处于最小缩放尺度,将实现双击放大,否则双击缩小,这和苹果官方应用相册的效果一样。
在类中添加下面两个方法:
func setupGestureRecognizer() { let doubleTap = UITapGestureRecognizer(target: self, action: "handleDoubleTap:") doubleTap.numberOfTapsRequired = 2 scrollView.addGestureRecognizer(doubleTap) } func handleDoubleTap(recognizer: UITapGestureRecognizer) { if (scrollView.zoomScale > scrollView.minimumZoomScale) { scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true) } else { scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true) } }
setupGestureRecognizer()
在上面的代码中,我们为UIScrollView添加了一个双击手势的监听,然后根据图片当前的缩放比例,来判断是将图片放大或者缩小。
运行程序,你会发现已经能通过双击手势来缩放图片了。
用可视化方式创建UIScrollView
使用storyboard可以实现和我们上面使用代码方式实现的同样的功能,而且更为简单,代码量更少。
在Main.storyboard文件中,拖一个新的视图控制器,并将其设置为初始控制器(既可以将箭头拖到新控制器上,也可以在属性选项卡中选中Is Initial View Controller复选框)。
拖一个UIScrollView到新的控制器中,然后设置其边缘始终粘着屏幕。
然后拖一个UIImageView到刚才的UIScrollView中,将它的边缘设置为粘着UIScrollView。
要记住UIScrollView需要知道它的内容的大小,才可以实现滚动。当你为UIImageView设置图片时,UIScrollView的内容大小就会被自动设置为图片的大小。
在UIImageView的属性选项卡中,将Image属性设置为image.png,然后通过updating the frames解决自动布局的问题。运行程序,你会发现已经实现图片的滚动显示了,并且没有敲一行代码。你还可以在UIScrollView的属性选项卡中查看还有哪些属性可以设置,比如可以设置最大和最小的缩放比例。
如果想要实现缩放功能,你仍然需要通过代码,设置代理并实现viewForZoomingInScrollView()方法,同我们之前做过的一样,就不再重复一遍了。
UIScrollView的嵌套
可以在一个UIScrollView中嵌套另一个UIScrollView,两个UIScrollView既可以是相同方向滚动的,也可以是不同方向的。这部分内容的示例代码请使用NestedScrollViews项目。
相同放下的滚动
相同方向的UIScrollView嵌套是指一个UIScrollView,它有另一个UIScrollView作为子控件,并且它们的滚动方向一致。你可以用相同方向的嵌套来实现这样的效果,比如在UIScrollView中添加多组要区分开的数据,你还可以通过它来实现两个UIScrollView同时滚动时的视差效果。在我们的示例中,我们将两个相同方向的UIScrollView设置不同的滚动速度,从而实现滚动时的视差效果。
打开NestedScrollViews项目中的storyboard文件,你将看到两个UIScrollView,分别叫做foreground和background。background里面添加了一个UIImageView,并将图片设置为image.png,foreground里面添加了一些标签和一个作为容器用的UIVIew,这些标签只是为了方便我们观看视图的滚动,容器视图我们将在下一节内容中才用到。
在最前面的滚动视图中,有一些label和一个容器视图。所以当我们运行应用程序的时候,只有一些label在滚动视图中。容器视图用于接下来的交叉滚动区域。如果你想知道视图控制器的最长尺寸,你可以通过选择试图控制器设置选项进行设置,调出尺寸检查器,改变模拟器的尺寸,自由设置大小。对我们而言,把高度调为1200.这仅仅帮助你在视觉上,你致力于你的看法和更多的空间在视图控制器上。它不影响程序的运行。它方便你布局元素,当你第一次运行的时候,就像在滚动视图下半部的元素。
我们的界面这样就算搭建完成了,现在运行程序的话,你会发现只有foreground视图在滚动,而background视图保持不动。接下来我们将要实现background的滚动,并且实现滚动的视差效果。
@IBOutlet weak var background: UIScrollView! @IBOutlet weak var foreground: UIScrollView!
改变ViewController类的声明,如下:
class ViewController: UIViewController, UIScrollViewDelegate {
foreground.delegate = self
func scrollViewDidScroll(scrollView: UIScrollView) { let foregroundHeight = foreground.contentSize.height - CGRectGetHeight(foreground.bounds) let percentageScroll = foreground.contentOffset.y / foregroundHeight let backgroundHeight = background.contentSize.height - CGRectGetHeight(background.bounds) background.contentOffset = CGPoint(x: 0, y: backgroundHeight * percentageScroll) }
交叉方向的UIScrollView嵌套是指一个UIScrollView,它有另一个UIScrollView作为子控件,并且它们的滚动方向正好相差90°,接下来我们就演示一下这种情况。
在NestedScrollViews项目中,你会发现在foreground里面有一个Container View,我们将用它来设置我们水平滚动的UIScrollView。
在storyboard中新拖入一个控制器,按住Control键从Container View拖到新的控制器,选择embed方式。然后选中这个控制器,将它的Size选项设为Freeform并将其高度设为128,因为Container View的高度就是128。往新控制器中拖入一个UIScrollView,设置其边缘始终粘着父控件。然后在UIScrollView中拖入一个70×70的UIView,将其背景色设为灰色方便我们观看,然后复制多个,从左到右依次摆放在UIScrollView中。你不需要精确地去设置每一个UIView的位置,接下来我会教你们怎么去做。现在我们的控制器界面应该是这个样子。
拖一个滚动视图到视图控制器中,并且四周约束如下:
然后添加一个视图滚动视图和尺寸检查器的大小设置为70×70。把它左侧的滚动视图。复制它来创建其他相似的看法和传播他们滚动视图。你不需要精确的间距。下面你将看到我在这一点上。我改变了视图的背景颜色浅灰色,这样他们更明显。
选择最左边的UIView,添加它上边和左边的约束,再添加宽度和高度约束。
再选择最右边的UIView,添加它的上边、右边、宽度和高度约束。
接下来,选中我们的UIScrollView,然后点击上面菜单栏中的Editor > Resolve Auto Layout Issues > All Views > Add Missing Constraints。这样我们所有的UIView就都添加好了约束。运行你的程序,竖直滚动到底部,你会看见我们的Container View,你可以水平滚动它里面的内容。下图中,我将控制器自身视图的背景色设置为透明,所以你看到的效果就是这样的。
我们的教程这就结束了,并没有包含UIScrollView所有的方方面面,但我希望通过这篇教程可以让你对UIScrollView有初步的了解,更多UIScrollView的知识,你可以查看苹果的官方文档:Scroll View Programming Guide.
你可以在 这里下载完整的程序作为参考。