StackView 布局并排并在彼此之上(如 zStack)

Posted

技术标签:

【中文标题】StackView 布局并排并在彼此之上(如 zStack)【英文标题】:StackView layout side by side & on Top of each other (like zStack) 【发布时间】:2021-01-07 12:35:36 【问题描述】:

我目前有这个界面,我将 MapView 和 UIView 放在一起。 UIView 是我放置图表叠加层的位置(例如:心率)。

当设备处于纵向模式时,这很好。但是,我想在横向模式下探索一种不同的布局,即 UIView(HR Chart) 和 MapView 并排。

这在 Interface Builder 中可行吗?还是仅在代码中? (我只想使用 UIKit 更好地支持较低的操作系统)

注意:我在 IB 中尝试过,但似乎无法弄清楚如何获取 Stack 以使 MapView 和 UIView 在纵向模式下相互重叠。

这就是现在的样子。但我想在横向模式下并排显示图表和地图。

编辑以澄清纵向模式下的当前布局和横向模式下的预期结果。 布局的样子。

【问题讨论】:

【参考方案1】:

这可以通过在堆栈视图上使用特征变化来完成,在垂直和水平之间更改轴。

考虑一下这个布局 - 我给标签设置了背景颜色来帮助可视化:

每个“名称/值”标签集都位于垂直堆栈视图中 “组”中的每个“对” - 功率/斜率、距离/经过时间、心率/节奏 - 也在各自的垂直堆栈视图中 这 3 个“对”位于水平堆栈视图中

选择RedPairStack,然后在“属性检查器”窗格中,单击轴左侧的+ 图标:

从该弹出窗口中,将 Width 更改为 Any 并将 Height 更改为 Compact 并单击 Add Variation

对于新的hC 变体,选择Horizontal

GreenPairStackBluePairStack 执行相同操作。

现在,当设备具有常规高度时,这三个堆栈视图将使用垂直轴...在紧凑高度中,它们将使用水平轴。

结果如下:

这里是 Storyboard 的来源,因此您可以检查所有内容:https://pastebin.com/dqNp8CcC


编辑 - 在 cmets...

要实现并排,我们需要进行一些更改:

看起来很相似,但是:

UIView - “PairsContainerView”(哈密瓜背景)中嵌入了“标签/值”水平堆栈 将该视图和地图视图嵌入垂直堆栈视图中

我从 PairStacks 中删除了 Trait 变体,然后将 Horizo​​ntalStack 约束到“PairsContainerView”的所有 4 个边

结果起初看起来不错,但旋转时标签会被拉长。因此,我们添加了几个新的特征变化。

首先,将所有标签的 Content Hugging Priority 设置为Required。

接下来,对于 Compact Height (hC) 特征,我们将 CenterY 约束添加到 Horizo​​ntalStack,并移除 Top 和 Bottom 约束。

以下是新结果:

并去除颜色:

由于您没有提供您希望布局外观的图片,这可能足以让您上路。

新故事板源:https://pastebin.com/TkVbNUVE


编辑 2

从上面的第一个例子开始...

添加一个UIView 作为地图视图的同级并将其自定义类分配给HeartRateView

这是我的快速HeartRateView(红色波浪线):

class HeartRateView: UIView 

    let shapeLayer = CAShapeLayer()
    
    override init(frame: CGRect) 
        super.init(frame: frame)
        commonInit()
    
    required init?(coder: NSCoder) 
        super.init(coder: coder)
        commonInit()
    
    
    func commonInit() -> Void 
        layer.addSublayer(shapeLayer)
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 1.0

        let pth = UIBezierPath()
        let topY: CGFloat = 40.0
        var pt: CGPoint = CGPoint(x: 0.0, y: topY)
        pth.move(to: pt)
        for _ in 1...30 
            pt.x += 2
            pt.y = topY + CGFloat.random(in: 0...40)
            pth.addLine(to: pt)
        
        
        shapeLayer.path = pth.cgPath
    


将这些约束添加到 HeartRateView:

顶部等于 MapView 顶部 前导等于 MapView 前导 宽度等于 MapView 宽度 高度等于 MapView 高度

如您所料,这将“叠加”在 MapView 上的 HeartRateView。

现在,在 Storyboard 中,将 Orientation 切换为 Landscape,然后单击“Vary for Traits”按钮。从该弹出窗口中,选择Height

在 Document Outline 窗格中选择 MapView,然后在 Size Inspector 窗格中选择 Trailing to Safe Area 和 Leading to Heart Rate View 约束:

点击键盘上的删除 - 它会删除此 Trait 变体的那些约束。应该是这样的:

你的视图应该是这样的:

选择心率视图并将其尾随限制到安全区域(我在所有内容上都使用 4 点填充)。您的视图现在应该如下所示(心率视图有清晰的背景,所以我们只能看到选择句柄):

现在添加一个从 MapView Trailing 到 HeartRateView Leading 的约束(小填充为 4):

因为 HeartRateView 仍然被限制为 MapView 的 Equal Width,所以它们会自动填充宽度。

最后一步,点击完成变化

现在我们得到这个输出:

这是第三个故事板来源:https://pastebin.com/2hPXisAH

【讨论】:

OP想要实现横向并排的布局,所以我认为地图应该在屏幕的右侧,仪表盘在屏幕的左侧。 @donMag 首先。非常令人印象深刻。我花了几天时间才在横向模式下将功率/斜率等设置为 1 行)正确。 (我对 IB 的仇恨越来越大:-p)。第二,我很抱歉(如果)你花了这么多时间,但我实际上希望“mapView”和“UIView”在横向模式下并排。 (也许而且很可能我的描述不是那么好)。在纵向模式下,MapView 和 UIView(带有 HR 图表)彼此重叠。在横向模式下,我想探索它们并排..(注意:我没有看过你的源代码) @myjunk - 啊,以为你在展示你想要的布局。请参阅我的答案的编辑。 @DonMag - 当我描述心率图 (Uiview) 位于 MapView 顶部(如 zstack)时,我添加了视图的外观图片。 UIView 是透明的,可以绘制用户的心率数据(屏幕截图上那些波浪状的红线)。它与地图视图的大小完全相同。我添加了一个侧视图来显示当前布局。 ?抱歉给您带来了困惑。 @myjunk - 如果您仍在尝试使此布局正常工作...一般的想法是为 HeartRateView 创建 2 个主要约束:一个用于地图可见,一个用于隐藏地图。当地图可见时将“MapVisible”约束设置为更高的优先级,当地图隐藏时将“MapHidden”约束设置为更高的优先级。我在这里放了一个更新的 Storyboard 和相关的 View Controller 代码:gist.github.com/DonMag/1ddc83e04584756ad750855b16a14ba1

以上是关于StackView 布局并排并在彼此之上(如 zStack)的主要内容,如果未能解决你的问题,请参考以下文章

如何将 2 个布局放在彼此之上

如何垂直自动布局 UIViews 列表,一个在彼此之上?

检测屏幕方向变化(快速)

浮动布局 - 项目从上到下运行,而不是并排?

无论浏览器大小如何,图片都需要并排放置

2 个按钮并排