UIScrollView首次渲染时内容原点对齐Bounds中心,原因是什么?
问题描述
我正在学习UIKit,尝试实现一个支持图片平移缩放的UIScrollView。首次渲染时,图片原点与UIScrollView的Bounds中心重合,双击重置缩放后该问题自动消失。
实现代码
import UIKit import SnapKit import SMScrollView open class PinchableImageVC: UIViewController, UIScrollViewDelegate { private let dataSource: String private let minimumZoomScale: CGFloat = 1.0 private let maximumZoomScale: CGFloat = 20.0 lazy internal var scrollView: SMScrollView = { [unowned self] in let scrollView = SMScrollView(frame: .zero) scrollView.minimumZoomScale = self.minimumZoomScale scrollView.maximumZoomScale = self.maximumZoomScale scrollView.zoomScale = 1.0 scrollView.bouncesZoom = true scrollView.isPagingEnabled = false scrollView.delegate = self scrollView.showsHorizontalScrollIndicator = false scrollView.decelerationRate = UIScrollView.DecelerationRate.fast scrollView.backgroundColor = .systemMint scrollView.stickToBounds = true scrollView.fitOnSizeChange = false return scrollView }() lazy internal var imageView: UIImageView = { guard let image = UIImage(named: dataSource) else { fatalError("assets.xcassets doesn't include any image named '\(dataSource)'.") } let imageView = UIImageView(image: image) return imageView }() internal init(dataSource: String) { self.dataSource = dataSource super.init(nibName: nil, bundle: nil) } public required init(coder: NSCoder) { fatalError("Uh uh you're not expected to invoke \(#function)") } open override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemRed view.addSubview(self.scrollView) scrollView.addSubview(self.imageView) scrollView.snp.makeConstraints { make in make.left.top.right.bottom.equalTo(self.view) } } open override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() imageView.frame = scrollView.bounds } public func viewForZooming(in scrollView: UIScrollView) -> UIView? { return self.imageView } }
修复方案
问题根源是首次渲染时未正确计算图片的初始位置与尺寸,以及contentOffset设置缺失。以下是具体修复步骤:
- 移除
viewDidLayoutSubviews中直接将imageView.frame设为scrollView.bounds的代码,改为根据图片尺寸和scrollView容器计算适配逻辑。 - 在
viewDidLayoutSubviews中添加图片居中适配逻辑,计算初始缩放比例、图片帧位置、scrollView的contentSize和contentOffset。
修改后的viewDidLayoutSubviews代码:
open override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard let image = imageView.image else { return } // 计算图片适配scrollView的初始缩放比例 let scrollViewBounds = scrollView.bounds let imageSize = image.size let widthScale = scrollViewBounds.width / imageSize.width let heightScale = scrollViewBounds.height / imageSize.height let initialScale = min(widthScale, heightScale) // 设置初始缩放比例 scrollView.zoomScale = initialScale // 计算图片的适配帧 let scaledWidth = imageSize.width * initialScale let scaledHeight = imageSize.height * initialScale imageView.frame = CGRect( x: (scrollViewBounds.width - scaledWidth) / 2, y: (scrollViewBounds.height - scaledHeight) / 2, width: scaledWidth, height: scaledHeight ) // 更新scrollView的contentSize scrollView.contentSize = CGSize(width: scaledWidth, height: scaledHeight) // 设置contentOffset确保图片居中 scrollView.contentOffset = CGPoint( x: max(0, (scaledWidth - scrollViewBounds.width) / 2), y: max(0, (scaledHeight - scrollViewBounds.height) / 2) ) }
另外,可根据实际需求调整SMScrollView的stickToBounds属性,若该属性会干扰内容位置计算,可暂时设为false,适配完成后再根据业务逻辑调整。
内容的提问来源于stack exchange,提问作者Niccolò Della Rocca
相关产品推荐
相关产品推荐

