Ios 如何在使用自动布局时使UIScrollView仅向一个方向缩放

Ios 如何在使用自动布局时使UIScrollView仅向一个方向缩放,ios,swift,uiscrollview,autolayout,zooming,Ios,Swift,Uiscrollview,Autolayout,Zooming,我试图使UIScrollView只允许在水平方向上缩放。滚动视图采用纯自动布局方法设置。通常的方法如许多堆栈溢出答案(例如)和资源中所建议的那样。在自动布局存在之前,我已经成功地在应用程序中使用了它。方法如下:在滚动视图的内容视图中,我覆盖setTransform(),并将传入的变换修改为仅在x方向缩放: override var transform: CGAffineTransform { get { return super.transform } set {

我试图使UIScrollView只允许在水平方向上缩放。滚动视图采用纯自动布局方法设置。通常的方法如许多堆栈溢出答案(例如)和资源中所建议的那样。在自动布局存在之前,我已经成功地在应用程序中使用了它。方法如下:在滚动视图的内容视图中,我覆盖
setTransform()
,并将传入的变换修改为仅在x方向缩放:

override var transform: CGAffineTransform {
    get { return super.transform }
    set {
        var t = newValue
        t.d = 1.0
        super.transform = t
    }
}
我还重置了滚动视图的内容偏移,使其在缩放过程中不会垂直滚动:

func scrollViewDidZoom(scrollView: UIScrollView) {
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
}
这在不使用自动布局时非常有效:

但是,使用“自动布局”时,内容偏移在缩放过程中会出错。虽然内容视图仅在水平方向上缩放,但它会垂直移动:

我举了一个例子项目(用来制作这个问题中的视频)。它包含两个情节提要:Main.storyboard和NoAutolayout.storyboard,它们完全相同,只是Main.storyboard启用了自动布局,而NoAutolayout未启用。通过更改主界面项目设置在它们之间切换,您可以看到这两种情况下的行为

我真的不想关闭autolayout,因为它以一种比手动布局更好的方式解决了许多实现UI的问题。在使用autolayout进行缩放时,是否有方法保持垂直内容偏移正确(即零)


编辑:我向示例项目添加了第三个情节提要AutolayoutVariableColumns.storyboard。这将添加一个文本字段来更改GridView中的列数,从而更改其固有的内容大小。这更接近地显示了我在引发这个问题的真实应用程序中需要的行为。

我不确定这是否满足您100%自动布局的要求,但这里有一个解决方案,其中
UIScrollView
设置了自动布局,并将
gridView
添加为子视图

您的
ViewController.swift
应如下所示:

// Add an outlet for the scrollView in interface builder.
@IBOutlet weak var scrollView: UIScrollView!

// Remove the outlet and view for gridView.
//@IBOutlet var gridView: GridView!

// Create the gridView here:
var gridView: GridView!

// Setup some views
override func viewDidLoad() {
    super.viewDidLoad()
    // The width for the gridView is set to 1000 here, change if needed.
    gridView = GridView(frame: CGRect(x: 0, y: 0, width: 1000, height: self.view.bounds.height))
    scrollView.addSubview(gridView)
    scrollView.contentSize = CGSize(width: gridView.frame.width, height: scrollView.frame.height)
    gridView.contentMode = .ScaleAspectFill
}

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return gridView
}

func scrollViewDidZoom(scrollView: UIScrollView) {
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
}

尝试设置
translatesAutoResizengMaskintoConstraints

func scrollViewDidZoom(scrollView: UIScrollView) {
    gridView.translatesAutoresizingMaskIntoConstraints = true
}
在示例项目中,它起作用。如果这还不够,请尝试在
ScrollViewDiEndZooming:
中以编程方式创建新约束

此外,如果这没有帮助,请更新示例项目,以便我们可以使用变量
intrinsicContentSize()

这篇由Ole Begemann撰写的文章给了我很大的帮助

WWDC 2015视频

幸运的是,这里有一面旗帜。它被称为translatesAutoResizengMask[无空间]约束

这有点多,但基本上是言行一致。它使视图的行为方式与传统布局系统下相同,但在自动布局世界中


我想我明白了。您需要在变换中应用平移以偏移缩放时UIScrollView尝试执行的居中操作

将其添加到GridView:

var unzoomedViewHeight: CGFloat?
override func layoutSubviews() {
    super.layoutSubviews()
    unzoomedViewHeight = frame.size.height
}

override var transform: CGAffineTransform {
    get { return super.transform }
    set {
        if let unzoomedViewHeight = unzoomedViewHeight {
            var t = newValue
            t.d = 1.0
            t.ty = (1.0 - t.a) * unzoomedViewHeight/2
            super.transform = t
        }
    }
}

要计算变换,我们需要知道视图的未缩放高度。这里,我只是在layoutSubviews()期间获取帧大小,并假设它包含未缩放的高度。可能有一些边缘情况是不正确的;在视图更新周期中,计算高度可能是一个更好的方法,但这是基本思想。

尽管@Anna的解决方案可行,但UIScrollView提供了一种使用自动布局的方法。但由于滚动视图的工作方式与其他视图略有不同,因此约束的解释也不同:

  • 滚动视图的边/边距与其内容之间的约束附加到滚动视图的内容区域
  • 高度宽度中心之间的约束附加到滚动视图的框架
  • 滚动视图和滚动视图外部视图之间的约束与普通视图类似
因此,在将子视图添加到固定到其边缘/边距的滚动视图时,该子视图将成为滚动视图的内容区域或内容视图

苹果建议在以下方面采取以下方法:

  • 将滚动视图添加到场景中
  • 绘制约束以定义滚动视图的大小和位置,与正常情况相同
  • 将视图添加到滚动视图中。将视图的Xcode特定标签设置为内容视图
  • 将内容视图的上边缘、下边缘、前缘和后缘固定到滚动视图的相应边缘。内容视图现在定义了 滚动视图的内容区域
  • (……)
  • (……)
  • 在内容视图中布局滚动视图的内容。使用约束将内容放置在“内容”视图中,使其与普通视图一样
  • 在您的情况下,GridView必须位于内容视图内:


    您可以保留已使用但现在已附加到内容视图的栅格视图约束。对于仅水平缩放,请使代码保持原样。您的
    转换
    覆盖处理得非常好。

    谢谢您的回答。不幸的是,我想使用autolayout的全部原因是“GridView”(在我的示例项目中实际上没有GridView那么简单)的大小会根据其内容而变化。Autolayout对
    intrinsicContentSize()
    的支持非常适合于此。如果没有它,视图必须随着内容的更改而手动调整大小,这相当痛苦。我并不介意关闭滚动视图的自动布局,只要它的内容视图可以通过其固有的内容大小来调整大小。啊,明白了。如果我想办法在
    gridView
    上使用autolayout,我会告诉你的!谢谢你的回答。我不完全理解为什么会这样,但我认为它本质上是一个脆弱的h