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

SwiftUI中UIViewRepresentable实现的图片缩放视图加载后空白及控制台异常日志问题

Hey there! Let's dive into why your ViewerFrameView is showing a blank screen and throwing those weird error logs.

What's Causing the Problem?

From the error messages and your code, the root issue is zero-value zoom scale calculations that break UIScrollView's internal assertions and matrix transformations:

  • The Must be called with non-zero scale error happens because when your image finishes loading, the ImageScrollView hasn't finished laying out yet. Its bounds.size is still (0,0), so when you calculate xScale = boundsSize.width / imageSize.width, you end up with 0.
  • The CGAffineTransformInvert: singular matrix error is a side effect of that zero scale—you can't invert a matrix with a zero scaling factor, which UIScrollView tries to do under the hood.

Also, your ZoomableScrollView initializes scrollView = ImageScrollView() upfront with a zero frame. By the time the image loads, the scroll view might not have received its actual layout bounds from SwiftUI yet.

Fixes to Get Your Image Showing & Scrolling

Let's fix this step by step:

1. Add Safety Checks to Zoom Scale Calculation

Update setCurrentMaxandMinZoomScale to skip invalid calculations when sizes are zero, and set safe default scales instead:

func setCurrentMaxandMinZoomScale() {
    let boundsSize = self.bounds.size
    guard let imageSize = imageZoomView.image?.size, 
          imageSize.width > 0, 
          imageSize.height > 0,
          boundsSize.width > 0, 
          boundsSize.height > 0 else {
        // Fallback to safe defaults if sizes are invalid
        self.minimumZoomScale = 1.0
        self.maximumZoomScale = 3.0
        return
    }
    
    let xScale = boundsSize.width / imageSize.width
    let yScale = boundsSize.height / imageSize.height
    let minScale = min(xScale, yScale)
    var maxScale: CGFloat = 1.0
    
    if minScale < 0.1 {
        maxScale = 0.3
    } else if minScale >= 0.1 && minScale < 0.5 {
        maxScale = 0.7
    } else {
        maxScale = max(1.0, minScale)
    }
    
    self.minimumZoomScale = minScale
    self.maximumZoomScale = maxScale
}

2. Explicitly Set ImageZoomView's Frame

When you assign the image to imageZoomView, make sure its frame matches the image's size so bounds calculations are correct:

func set(image: UIImage) {
    imageZoomView?.removeFromSuperview()
    imageZoomView = UIImageView(image: image)
    // Force the image view to match the image's dimensions
    imageZoomView.frame = CGRect(origin: .zero, size: image.size)
    self.addSubview(imageZoomView)
    configurateFor(imageSize: image.size)
}

3. Recalculate Scales After Layout

Update layoutSubviews to recheck zoom scales every time the scroll view's bounds change (like after SwiftUI finishes laying it out):

override func layoutSubviews() {
    super.layoutSubviews()
    // Refresh scales now that we have valid bounds
    setCurrentMaxandMinZoomScale()
    centerImage()
}

4. Fix ZoomableScrollView's Initialization

Don't create the scroll view upfront—make it in makeUIView instead, and only update the image if it actually changes:

struct ZoomableScrollView: UIViewRepresentable {
    @Binding var image: UIImage
    
    func makeUIView(context: Context) -> ImageScrollView {
        let scrollView = ImageScrollView()
        // Set initial image if it's already available
        if !image.size.equalTo(.zero) {
            scrollView.set(image: image)
        }
        return scrollView
    }
    
    func updateUIView(_ uiView: ImageScrollView, context: Context) {
        // Avoid redundant updates if the image hasn't changed
        if uiView.imageZoomView?.image != image {
            uiView.set(image: image)
        }
    }
}

5. Guard Against Empty Images in ViewerFrameView

Only update the local image state when the view model's image is valid:

.onChange(of: viewModel.image, perform: { newValue in
    guard !newValue.size.equalTo(.zero) else { return }
    image = newValue
    print(newValue.size)
})

Test It Out

After making these changes, your image should load correctly without the error logs. The scroll view will wait until it has valid bounds before calculating zoom scales, and all matrix operations will use valid non-zero values.

内容的提问来源于stack exchange,提问作者Iskander Nizamov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 15:09:08