SwiftUI:如何检测两个视图是否相交?

SwiftUI: How can I detect if two views are intersecting each other?

一个视图与另一个视图相交。我们如何在 SwiftUI 中检测到这个交叉点?

在 Swift 中,我将通过几行代码实现这一点:

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    let a = UIView(frame: CGRect(x: 100, y: 100, width: 150, height: 150))
    a.backgroundColor = .purple
    view.addSubview(a)

    let b = UIView(frame: CGRect(x: 150, y: 150, width: 150, height: 150))
    b.backgroundColor = .orange
    view.addSubview(b)

    if a.frame.intersects(b.frame) {
        print("yes!")
    } else {
        print("no intersection!")
    }

  }

}

显然,它与SwiftUI无关:我们这里有另一个框架的概念。什么可以代替?有人有想法吗?

更新:

非常感谢 user3441734 and Asperi 的帮助和建议! 这是我尝试使用矩形数组解决问题的尝试:

import SwiftUI

struct ContentView: View {

@State var rect = CGRect()
@State var aRects = [CGRect]()


var body: some View {

    VStack {
        ZStack {
            Rectangle()
                  .frame(width: 150, height: 300)
                  .foregroundColor(.purple)
                  .background(self.rectReader(self.$rect))
                  HStack {
                      Spacer()
                      Spacer()
                      Spacer()
                      Rectangle()
                          .frame(width: 150, height: 180)
                          .foregroundColor(.pink)
                         .background(self.rectReader(self.$rect))
                      Spacer()
                  }
            }

        Button(action: {
             //print("aRects: \(self.aRects)")
             self.checkIntersection()
         }){
            Text("Check Intersection!")
         }
    }



}


// fill array of rects here
func rectReader(_ binding: Binding<CGRect>) -> some View {
    return GeometryReader { (geometry) -> AnyView in
        let rect = geometry.frame(in: .global)
        DispatchQueue.main.async {
            binding.wrappedValue = rect
            print("rect: \(self.rect)")
            self.aRects.append(self.rect)
        }
        return AnyView(Rectangle().fill(Color.clear))
    }
}

func checkIntersection() {
    if aRects.count == 2 {
        if self.aRects[0].intersects(self.aRects[1]) {
            print("yes!")
        } else {
            print("no!")
        }
    }
}

}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
    ContentView()
}
}

你的问题一点都不傻!

好像很简单

  1. 读取第一个视图的帧(在全局坐标中)并将其保存在首选项中
  2. 读取第二个视图的帧(在全局坐标中)并将其保存在首选项中
  3. 当偏好改变时计算交集

创建我们的首选项键结构很容易,之前已经在此网站上进行了解释(使用搜索了解更多详细信息:-))

struct Sizes: PreferenceKey {
    typealias Value = [CGRect]
    static var defaultValue: [CGRect] = []

    static func reduce(value: inout [CGRect], nextValue: () -> [CGRect]) {
        value.append(contentsOf: nextValue())
    }
}

已经解释了在后台视图修改器中使用 GeometryReader

struct SizeReader: View {
    var body: some View {
        GeometryReader { proxy in
            Color.clear
            .preference(key: Sizes.self, value: [proxy.frame(in: .global)])
        }
    }
}

现在我们可以使用它在我们的尺寸首选项键中保存框架矩形

RectView(color: .pink).background(SizeReader())

我们的 RectView 呢?为了演示我们的 "easy solution" 是如何工作的,假设有人用随机大小和随机对齐方式创建它

struct RectView: View {
    let color: Color
    let size = CGFloat(Int.random(in: 50 ... 200))
    var body: some View {
        color.frame(width: size, height: size)
            .alignmentGuide(HorizontalAlignment.center) {
                CGFloat.random(in: 0 ..< [=13=].width)
            }
            .alignmentGuide(VerticalAlignment.center) {
                CGFloat.random(in: 0 ..< [=13=].width)
            }
    }
}

到目前为止一切顺利,我们准备检查我们的 "easy solution"

让我们用

完成我们的项目
struct ContentView: View {
    @State var id = UUID()
    var body: some View {
        VStack {
            ZStack {
                Color.black
                RectView(color: .pink)
                    .background(SizeReader())
                RectView(color: .yellow)
                    .background(SizeReader())
                    .blendMode(.difference)
            }
            .onPreferenceChange(Sizes.self) { (value) in
                let intersection = value[0].intersection(value[1])
                print(value[0])
                print(value[1])
                print(intersection)
                print()
            }
            .onTapGesture {
                self.id = UUID()
            }
            Text("paceholder").id(id)
        }
    }
}

运行 它和每次单击屏幕的黑色部分时,您会看到两个大小和位置随机的矩形,并且在调试 window 打印输出中我们感兴趣的值。

警告 检查打印输出,如果有误,您会看到一些东西!

代码是错误的例子,我把它放在这里是为了证明,你的问题一点也不愚蠢!关于 SwiftUI 布局及其在幕后的工作原理,有很多想法需要理解。

希望有高人指点一下问题所在,并指导我们如何让它正常运行,欢迎各位高手!请不要更改 RectView,认为它是一些内置的 SwiftUI 组件