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

SwiftUI/UIKit:使用UIScrollView实现仅水平方向缩放滚动视图问题

问题原因分析

移除.flexibleHeight后图片无法显示的原因

  • 原代码中hostedView依赖autoresizingMask适配UIScrollView的尺寸,移除.flexibleHeight后,hostedView高度不会跟随UIScrollView的布局变化自动调整,初始设置的frame会在后续布局刷新后失效。
  • 你使用了.scaledToFit()修饰的图片,会优先按照宽高比适配容器尺寸,当hostedView高度约束缺失或冲突时,SwiftUI布局系统会将内容压缩至0高度,导致图片不可见。

垂直方向同步缩放的原因

UIScrollView默认的缩放逻辑是对可缩放视图应用均匀的transform变换,X/Y轴使用相同的缩放系数,所以缩放时会同步修改垂直方向尺寸。

解决方案

我们需要修改默认缩放逻辑,仅对水平方向应用缩放系数,垂直方向固定为原始高度,同时限制滚动仅在水平方向生效:

  1. 用自动布局固定hostedView的高度和UIScrollView高度一致,移除垂直方向的自适应规则
  2. 在UIScrollViewDelegate的scrollViewDidZoom回调中,手动设置hostedView的变换,仅应用X轴缩放
  3. 手动调整UIScrollView的contentSize,高度固定和滚动视图一致,仅宽度随缩放比例变化

修改后的完整代码

struct ContentView: View {
    var body: some View {
        ZoomableScrollView {
            Image(systemName: "star")
                .resizable()
                .scaledToFit()
                .frame(maxHeight: .infinity) // 垂直方向填充满容器
          }
    }
}

struct ZoomableScrollView<Content: View>: UIViewRepresentable {
  private var content: Content

  init(@ViewBuilder content: () -> Content) {
    self.content = content()
  }

  func makeUIView(context: Context) -> UIScrollView {
    let scrollView = UIScrollView()
    scrollView.delegate = context.coordinator
    scrollView.maximumZoomScale = 20
    scrollView.minimumZoomScale = 1
    scrollView.bouncesZoom = true
    // 禁止垂直方向滚动和指示器
    scrollView.alwaysBounceVertical = false
    scrollView.showsVerticalScrollIndicator = false

    let hostedView = context.coordinator.hostingController.view!
    hostedView.translatesAutoresizingMaskIntoConstraints = false
    hostedView.backgroundColor = .clear // 避免背景遮挡
    scrollView.addSubview(hostedView)
    
    // 固定高度和ScrollView一致
    NSLayoutConstraint.activate([
        hostedView.topAnchor.constraint(equalTo: scrollView.topAnchor),
        hostedView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
        hostedView.heightAnchor.constraint(equalTo: scrollView.heightAnchor),
    ])

    return scrollView
  }

  func makeCoordinator() -> Coordinator {
    return Coordinator(hostingController: UIHostingController(rootView: self.content))
  }

  func updateUIView(_ uiView: UIScrollView, context: Context) {
    context.coordinator.hostingController.rootView = self.content
    assert(context.coordinator.hostingController.view.superview == uiView)
  }

  class Coordinator: NSObject, UIScrollViewDelegate {
    var hostingController: UIHostingController<Content>

    init(hostingController: UIHostingController<Content>) {
      self.hostingController = hostingController
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
      return hostingController.view
    }
    
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        let scale = scrollView.zoomScale
        // 仅应用水平方向缩放,垂直方向保持原始比例
        hostingController.view.transform = CGAffineTransform(scaleX: scale, y: 1)
        // 调整内容尺寸,仅宽度随缩放变化
        let contentWidth = hostingController.view.intrinsicContentSize.width * scale
        scrollView.contentSize = CGSize(
            width: contentWidth,
            height: scrollView.bounds.height
        )
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 23:06:04