简体   繁体   English

使用 DragGesture 调整 SwiftUI 视图的框架会在所有维度上调整其大小

[英]Adjusting the frame of a SwiftUI view with a DragGesture resizes it in all dimensions

I'm trying to implement a simple view in SwiftUI that can be resized via a drag handle placed on its lower-left corner.我正在尝试在 SwiftUI 中实现一个简单的视图,可以通过放置在其左下角的拖动手柄调整其大小。 The resizable view's dimensions are set via two @State variables controlling the width and height.可调整大小的视图的尺寸是通过两个控制宽度和高度的@State 变量设置的。 These variables are mutated within a DragGesture's onChanged handler captured by the drag handle.这些变量在拖动手柄捕获的 DragGesture 的 onChanged 处理程序中发生变化。

It's sort of working, but not in the way I expect.它有点工作,但不是我期望的方式。 When I perform a drag gesture on the drag handle, the view gets resized in all dimensions.当我在拖动手柄上执行拖动手势时,视图会在所有维度上调整大小。 I want the resizing to work in the same way as a desktop window;我希望调整大小的工作方式与桌面 window 相同; dragging shouldn't resize the view in all directions.拖动不应在所有方向上调整视图的大小。

Below is a recording from the iOS simulator showing the incorrect behaviour:以下是来自 iOS 模拟器的记录,显示了不正确的行为:

调整视图大小

I understand that the centre of a SwiftUI view is considered its origin, and that's probably what's causing this behaviour.我知道 SwiftUI 视图的中心被认为是它的起源,这可能是导致这种行为的原因。 I'm not sure how to work around this.我不确定如何解决这个问题。

Here's my code:这是我的代码:

import SwiftUI

struct ContentView: View {
    // Initialise to a size proportional to the screen dimensions.
    @State private var width = UIScreen.main.bounds.size.width / 3.5
    @State private var height = UIScreen.main.bounds.size.height / 1.5
    
    var body: some View {
        // This is the view that's going to be resized.
        ZStack(alignment: .bottomTrailing) {
            Text("Hello, world!")
                .frame(width: width, height: height)
            // This is the "drag handle" positioned on the lower-left corner of this stack.
            Text("")
                .frame(width: 30, height: 30)
                .background(.red)
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            // Enforce minimum dimensions.
                            width = max(100, width + value.translation.width)
                            height = max(100, height + value.translation.height)
                        }
                )
        }
        .frame(width: width, height: height, alignment: .center)
        .border(.red, width: 5)
        .background(.yellow)
        .padding()
    }
}

Edit : I was too liberal in what I removed for the minimal example above.编辑:对于上面的最小示例,我删除的内容过于自由。 The following snippet adds a position constraint on the ZStack, which is used in another gesture (not shown) so the view can be dragged around.以下代码片段在 ZStack 上添加了一个position约束,它用于另一个手势(未显示),因此可以拖动视图。

I'm guessing this is problematic because position specifies relative coordinates from the centre of the ZStack to the parent VStack, and as the ZStack is resized, it's being "moved" to maintain the position constraint.我猜这是有问题的,因为position指定了从 ZStack 的中心到父 VStack 的相对坐标,并且随着 ZStack 的大小调整,它被“移动”以保持 position 约束。

import SwiftUI

struct ContentView: View {
    // Initialise to a size proportional to the screen dimensions.
    @State private var width = UIScreen.main.bounds.size.width / 3.5
    @State private var height = UIScreen.main.bounds.size.height / 1.5
    
    var body: some View {
        VStack {
            // This is the view that's going to be resized.
            ZStack(alignment: .bottomTrailing) {
                Text("Hello, world!")
                    .frame(width: width, height: height)
                // This is the "drag handle" positioned on the lower-left corner of this stack.
                Text("")
                    .frame(width: 30, height: 30)
                    .background(.red)
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                // Enforce minimum dimensions.
                                width = max(100, width + value.translation.width)
                                height = max(100, height + value.translation.height)
                            }
                    )
            }
            .frame(width: width, height: height)
            .border(.red, width: 5)
            .background(.yellow)
            .padding()
            // This position constraint causes the ZStack to grow/shrink in all dimensions
            // when resized.
            .position(x: 400, y: 400)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
    }
}

You're right that the issue is caused by your view being center-aligned.您是对的,问题是由您的视图居中对齐引起的。

To fix this, you can wrap your view in a VStack with a different alignment applied, eg .topLeading if you want it to align to the top-left.要解决此问题,您可以将视图包装在VStack中,并应用不同的.topLeading ,例如,如果您希望它与左上角对齐,则使用 .topLeading。

You also have to make sure this VStack is taking up the available space of the view.您还必须确保此VStack占用了视图的可用空间。 Otherwise, it will shrink to the size of your resizable box, causing the view to stay center-aligned.否则,它将缩小到可调整大小框的大小,导致视图保持居中对齐。 You can achieve this with .frame(maxWidth: .infinity, maxHeight: .infinity) .您可以使用.frame(maxWidth: .infinity, maxHeight: .infinity)来实现。

TL;DR长话短说

Place your resizable box in a VStack with a .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) modifier.使用 .frame .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)修饰符将可调整大小的框放置在VStack中。

VStack { // <-- Wrapping VStack with alignment modifier
    // This is the view that's going to be resized.
    ZStack(alignment: .bottomTrailing) {
        Text("Hello, world!")
            .frame(width: width, height: height)
        // This is the "drag handle" positioned on the lower-left corner of this stack.
        Text("")
            .frame(width: 30, height: 30)
            .background(.red)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        // Enforce minimum dimensions.
                        width = max(100, width + value.translation.width)
                        height = max(100, height + value.translation.height)
                    }
            )
    }
    .frame(width: width, height: height, alignment: .topLeading)
    .border(.red, width: 5)
    .background(.yellow)
    .padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)

Edit编辑

With an additional position modifier on the ZStack, try offsetting the x and y values by half of the width and height to position relative to the top-left origin of the ZStack:在 ZStack 上使用额外的position修饰符,尝试将xy值偏移widthheight的一半,使其相对于 ZStack 的左上原点为 position:

.position(x: 400 + width / 2, y: 400 + height / 2)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM