原文: appcoda.com (IOS Beginner's Guide to UIScrollView 由joyce echessa发布
翻译: 安明哲
说明: 转载时请注明出处
在IOS中,scroll view 本用作显示屏幕上不能完全装下的内容。它主要有两个作用:
- 用户可以通过拖动的方式显示更多内容
- 用户可以通过手势缩放现实的内容
IOS的公共控件UITbaleView就是继承与UIScrollView并提供了一种不错的方式去呈现内容(当这个内容大于屏幕尺寸)
在本节课程中,我们将从多方面了解ScrollView,特性,创建一个ScrollView(通过代码和通过Interface Builder),滚动和缩放,insets和outsets。
开始阅读之前,请首先下载本届课程的源代码。
译者注:
这里的下载需要自带梯子,如果没有梯子,可以从我的服务器获取
通过编码方式创建ScrollView
不管是通过代码或者Interface builder都可以在一个view中创建一个Scroll View,然后做一点点必要的配置就实现一个基本的ScrollView的功能了。
- 你必须设置
ContentSize
属性,此属性用于指定你要展现的内容的Size,IOS由此确认滚动区域。
- 你还必须添加一个或者多个View以供显示。
当然,还有许多可选的配置,垂直或是水平滚动,滑动、缩放的效果,滚动条的路径(滚动的方向)等等。
现在,我们开始通过代码创建一个ScrollView,打开 ScrollViewDemo(刚才下载的项目),仅仅是一个简单地Signle View,其中ScollerViewController这个类与Interface Builder中的UIViewController关联,并且此项目还包含了一张图片image.png (图片来自unsplash.com)
打开ScrollViewController.swift 并且添加如下代码:
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
//译者注:如果你是用的是swift2.x 这行代码会出现问题
scrollView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
scrollView.addSubview(imageView)
view.addSubview(scrollView)
}
译者注:
上面的代码中带注释的一行会在swift2下报错,在swift2.0中同时取得两个枚举值不再支持使用 | 运算符,而改用数组 ,所以swift2中正确的代码应该是这个样子
scrollView.autoresizingMask = [UIViewAutoresizing.FlexibleHeight,UIViewAutoresizing.FlexibleWidth]
上面的代码创建了一个scroll view和一张图片。其中ImageView是ScrollView的子view(有点绕);contentSize指定了可滚动的地带为图片的大小(2000 x 1500);scrollView的背景为黑色,设置autoresizeMask为.FlexibleWidth和 .FlexibleHeight以便于在屏幕旋转的时候scrollview与parsentView维持正确位置关系。运行这个app的时候你应该能够图片的没一部分。
当你运行的时候,你可能会注意到,通常你只能看到图片左上角的那一部分,就像下面这样:
这是因为scrollview的bound(默认)被设置为(0,0),也就是图片的左上角。如果你希望重新定位第打开app时图片的显示位置,你需要改变scrollview的bound,SrollerView有一个contentOffset属性可以帮助你实现这个需求。
添加如下代码到你的代码中,(注意这段代码应该在autoresizingMask之后):
scrollView.contentOffset = CGPoint(x: 1000, y: 450)
这个时候再次运行app你将看到scrollview已经移动到图皮的另一部分,这样当view被加载的时候,你就可以确定你要给用户呈现什么。
缩放
我们已经创建了一个scrollview并且允许用户通过滚动来控制一个较大尺寸的view,但是如果视图可以缩放,将进一步增强用户体验。
要支持缩放,你必须为view添加一个delegate,这个delegate必须遵从IScrollViewDelegate这个协议,并且必须要实现viewForZoomingInScrollView(),该方法返回一个view,这个view将可以在scrollview内缩放。
你依旧要做一点点的工作来支持缩放,你可以设置scrollvierw的 minimumZoomScale 和 maximumZoomScale属性(如果不设定这两个属性,他们将会有一个默认值-->1.0)
修改ScrollerView的定义如下:
class ScrollViewController: UIViewController, UIScrollViewDelegate
然后添加如下方法到类中:
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imageView
}
最后,添加如下代码到viewDidLoad()的底部:
译者注:主要就是绑定delegate,设置缩放的范围和步长
scrollView.delegate = self
scrollView.minimumZoomScale = 0.1
scrollView.maximumZoomScale = 4.0
scrollView.zoomScale = 1.0
上面的代码中设置了zoomScale为1.0用于指定最大缩放和最小缩放的缩放因子(默认情况下缩放的大小),当你运行app,你可以滚动并且缩放,我们设置最大缩放比例4.0,所以你最大可以放大这张图片到原始尺寸的4倍(最小缩放比例同理),但是当我们把图片放大的时候,图片会变得很很模糊,这并不是用户想要看到的,下一步中我们将让图片返回到1.0初始的状态。
在上边,我们设置minimumZoomScale为0.1以至于缩小之后返回一个很小的图片并且屏幕中出现了很多空白。我们想让图片自适应
要实现这个功能,我们需要根据scrollview的size和imageview的size计算最小缩放比例。
首先删除viewDidLoad里面的几行代码:
scrollView.minimumZoomScale = 0.1
scrollView.maximumZoomScale = 4.0
scrollView.zoomScale = 1.0
添加一个方法到类中,我们得到的宽度和高度的比例,并选择较小的两者,并设置为minimumScale。提示一下,我们删除了maxinmumZoomScale,所以他会被默认设置为1.0.
func setZoomScale(){
let imageViewSize = imageView.bounds.size
let scrollViewSize = scrollView.bounds.size
let widthSacle = scrollViewSize.width / imageViewSize.width
let heightSacle = scrollViewSize.height / imageViewSize.height
scrollView.minimumZoomScale = min(widthSacle, heightSacle)
scrollView.zoomScale = 1.0
}
然后再viewDidLoad()中调用这个方法
setZoomScale()
同时添加以下代码,以便在设备方向改变后图像依旧铺满屏幕。
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)
}
这个方法会在每次一缩放操作后执行,它告诉delegate,scrllowView的缩放比例发生了改变,上面的代码计算了padding并且根据padding重新设置了scrollview 内容的padding
通过连按缩放
默认情况下ScrollView通过少量的代码即可实现支持缩放(捏和撑)手势,但是支持更多的手势我们则需要再做一些工作。
IOS人机交互的借口定义了一种通过双击(连按)来进行缩放的方法。但是这个方法认为view的缩放级别是单一的(翻译起来有点绕)总之就是双击一次执行了放大操作后下一次会默认执行缩小操作。但是很多程序的交互行为是需要更灵活的双击缩放的,比如地图应用,需要不停地双击放大,而不是放大后再双击变成缩小。
在我们app中,我们将实现double-tap 把图片放到最大,而后双击再缩小
添加如下代码到类中:
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)
}
}
然后再viewDidLoad底部调用他们:
setupGestureRecognizer()
在上面的代码中,我们添加了一个gesture recognize(手势识别器),当用户双击之后,我们可以根据当前的缩放级别来进行放大和缩小。
这样,我们就可以通过双击放大和缩小图片了。
网友评论
如果现在scrollView的宽度是800 图片的宽度是1600 是不是要让图片按照(800/1600 = 0.5)的倍率缩放刚好让图片的宽度等于scrollview的宽度
那么高度也是一样的道理
现在有一张图片 2000 * 3000 的尺寸 scrollview的大小是500*1000
那么如果按照宽度缩放 那么最小缩放倍率是不是应该是 500/2000=0.25 倍 刚好宽度适应 此时图片缩放0.25倍 缩放后图片的高度是 3000*0.25 750 最终缩放后图片尺寸是 500*750
如果按照高度缩放 那么最小缩放倍率为 1000/3000=0.3倍 高度刚好等于屏幕高度 此时图片缩放0.3倍 缩放后图片的宽度是 500*0.3=150 最终得到的图片大小是 150*1000
想一下 500*750 和 150*1000 的区别 就明白 我们需要使用最短的尺寸来做图片的缩小咯
所以 设置最小的缩放倍率的时候使用了min()函数,取最小的倍率
scrollView.minimumZoomScale = min(widthSacle, heightSacle)