SwiftUI:如何检测两个视图是否相交? [英] SwiftUI: How can I detect if two views are intersecting each other?

查看:198
本文介绍了SwiftUI:如何检测两个视图是否相交?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

 导入UIKit类ViewController:UIViewController {覆盖func viewDidLoad(){super.viewDidLoad()让a = UIView(frame:CGRect(x:100,y:100,width:150,height:150))a.backgroundColor = .purpleview.addSubview(a)令b = UIView(frame:CGRect(x:150,y:150,width:150,height:150))b.backgroundColor = .orangeview.addSubview(b)如果a.frame.intersects(b.frame){打印(是!")} 别的 {打印(无路口!")}}} 

很显然,它与SwiftUI无关:我们在这里有了框架的另一种概念.什么可以代替呢?有想法吗?

更新:

非常感谢

解决方案

您的问题一点都不愚蠢!

这似乎很简单

  1. 读取第一个视图的框架(以全局坐标为单位)并将其保存在首选项中
  2. 读取第二个View的框架(以全局坐标为单位)并将其保存为首选项
  3. 当偏好设置发生变化时计算交集

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

 结构大小:PreferenceKey {typealias值= [CGRect]静态var defaultValue:[CGRect] = []静态函数reduce(value:inout [CGRect],nextValue:()-> [CGRect]){value.append(contentsOf:nextValue())}} 

已经解释了在背景视图修改器中使用GeometryReader

  struct SizeReader:查看{var body:some View {GeometryReader {代理在颜色透明.preference(key:Sizes.self,value:[proxy.frame(in:.global)])}}} 

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

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

那我们的RectView呢?为了演示我们的简单解决方案"的工作原理,可以想象有人用随机大小和随机对齐方式创建了它

  struct RectView:查看{让颜色:颜色let size = CGFloat(Int.random(in:50 ... 200))var body:some View {color.frame(宽度:大小,高度:大小).alignmentGuide(Horizo​​ntalAlignment.center){CGFloat.random(in:0 ..< $ 0.width)}.alignmentGuide(VerticalAlignment.center){CGFloat.random(in:0 ..< $ 0.width)}}} 

到目前为止,太好了,我们准备检查我们的简单解决方案"

让我们完成我们的项目

  struct ContentView:查看{@State var id = UUID()var body:some View {VStack {ZStack {颜色:黑色RectView(颜色:.pink).background(SizeReader())RectView(颜色:.yellow).background(SizeReader()).blendMode(.difference)}.onPreferenceChange(Sizes.self){(value)in让交集= value [0] .intersection(value [1])打印(值[0])打印(值[1])打印(交叉点)打印()}.onTapGesture {self.id = UUID()}Text("paceholder").id(id)}}} 

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

警告,检查打印输出,如果发现错误,您会发现一些东西!

该代码是有问题的示例,我将其放在此处以证明您的问题根本不是愚蠢的!关于SwiftUI布局及其在幕后的工作方式,有很多想法可以理解.

我希望有人会尝试解释问题所在,并指导我们如何使其发挥作用,欢迎所有专家!请不要更改RectView,认为它是一些内置的SwiftUI组件

One view is intersecting the other one. How could we detect this intersection in SwiftUI?

In Swift I would reach this with a few lines of code:

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!")
    }

  }

}

Obviously, it is not relevant for the SwiftUI: we have another concept of frames here. What could work instead? Has somebody an idea?

Update:

Many thanks for user3441734 and Asperi for their help and suggestions! Here is my attempt to solve the problem, using array of rectangles:

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()
}
}

解决方案

Your question is not stupid at all!

It seems to be easy

  1. read frame of first View (in global coordinates) and save it in preferences
  2. read frame of second View (in global coordinates) and save it in preferences
  3. calculate intersection when preferences changed

create our preference key structure is easy, it was already explained at this web site before (use search for more details :-))

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

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

using GeometryReader in background View modifier was already explained

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

and now we can use it to save frame rectangles in our Sizes preference key

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

What about our RectView ? To demonstrate how our "easy solution" works imagine that somebody create it with random size and random alignment

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 ..< $0.width)
            }
            .alignmentGuide(VerticalAlignment.center) {
                CGFloat.random(in: 0 ..< $0.width)
            }
    }
}

so far, so good, we are ready to check our "easy solution"

Lets finish our project with

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)
        }
    }
}

Run it and each time you click the black part of the screen you see two randomly sized and positioned rectangles and in the debug window printout of values ​​of our interest.

WARNING check the printout and you'll see that something if wrong!

The code is buggy example and I put it here to demonstrate, that your question is not stupid at all! There is a lot of thinks to understand about SwiftUI layout and how it works behind the scene.

I hope, somebody clever will try to explain what's wrong and will guide us on how to make it functional, all experts are welcome! Please don't change RectView, think that it is some build-in SwiftUI component

这篇关于SwiftUI:如何检测两个视图是否相交?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆