You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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设置缺失。以下是具体修复步骤:

  1. 移除viewDidLayoutSubviews中直接将imageView.frame设为scrollView.bounds的代码,改为根据图片尺寸和scrollView容器计算适配逻辑。
  2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 11:25:09