position() / offset() 问题 swiftUI

Problem with position() / offset() swiftUI

我正在尝试将这个圆移到屏幕的边缘,然后确定它是否在屏幕的边界并显示警报。

这是我已经尝试过的方法:尝试过 offset(),现在正在玩 position(),但运气不好。尝试使用 geometryReader 没有帮助

我可以硬编码为负值,但我想知道如何检测屏幕的边缘,并了解为什么这个逻辑不起作用。

为了了解屏幕的边缘,我也尝试使用 UIScreen.main.boundries

我正在做 SwiftUI 练习以使 SwiftUI 变得“舒服”,这真的很痛苦。

import SwiftUI

struct ContentView: View {
    let buttons1 = ["RESET","UP","COLOR"]
    let buttons2 = ["<","DOWN",">"]
    @State private var colorSelector = 0
    let circleColors:[Color] = [.red,.green,.blue,.black]
    @State private var ballOffset = CGPoint.init(x: 80, y: 80)
    @State private var circleOpacity = 0.5
    @State private var alertActive = false // wall problem 

    var body: some View {

            ZStack{
                Color.init("Grayish")
                VStack{
                    Circle()
                        .fill(circleColors[colorSelector]).opacity(circleOpacity)
                        .position(ballOffset)
                        .frame(width: 160, height: 160)
                }

                VStack(spacing:10){
                    Spacer()

                    Slider(value: $circleOpacity)

                    HStack{
                        ForEach(0..<buttons1.count,id:\.self){ text in
                            Button(action: {
                                switch text{
                                case 0:
                                    colorSelector = 0
                                    ballOffset = .zero

                                case 1:
                                    ballOffset.y -= 3

                                case 2:
                                    if colorSelector == circleColors.count - 1{
                                            colorSelector = 0
                                        }
                                        else {
                                            colorSelector += 1
                                        }
                                default: break
                                }

                            }, label: {
                                Text(buttons1[text])
                                    .foregroundColor(text == 1 ? Color.white:Color.accentColor)

                            })
                            .buttonModifier()
                            .background(RoundedRectangle(cornerRadius: 10).fill(Color.accentColor).opacity(text == 1 ? 1 : 0))

                        }
                    }.padding(.horizontal,5)

                    HStack{
                        ForEach(0..<buttons2.count,id:\.self){text in
                            Button(action:{
                                switch text{
                                case 0:
                                    ballOffset.x -= 3

                                case 1:
                                    ballOffset.y += 3

                                case 2:
                                    ballOffset.x += 3
                                default:break
                                }
                            },
                            label:{
                                Text(buttons2[text])
                                    .foregroundColor(Color.white)
                            })
                            .buttonModifier()
                            .background(RoundedRectangle(cornerRadius: 10).fill(Color.blue))
                        }
                    }.padding([.bottom,.horizontal],5)

                }
                .alert(isPresented: $alertActive, content: {
                    Alert(title: Text("out of bounds"), message: Text("out"), dismissButton: .default(Text("OK")))
                }) // alert not working have to figure out wall problem first
            }.edgesIgnoringSafeArea(.all)

    }
}

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

struct ButtonModifier: ViewModifier{
    func body(content: Content) -> some View {
        content
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 44, idealHeight: 44, maxHeight: 44)
            .padding(.horizontal)
            .foregroundColor(Color.accentColor)
            .background(RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.accentColor))

    }
}

extension View{
    func buttonModifier() -> some View{
        modifier(ButtonModifier())
    }
}

这是一个使用GeometryReader 的解决方案,它是动态的,会随着设备方向的变化而变化。它会在移动球之前检查未来球的位置是否在边界内,并在失败时显示警报。

struct ContentView: View {
    let buttons1 = ["RESET","UP","COLOR"]
    let buttons2 = ["<","DOWN",">"]
    @State private var colorSelector = 0
    let circleColors:[Color] = [.red,.green,.blue,.black]
    let ballSize = CGSize(width: 160, height: 160)
    @State private var ballOffset: CGSize = .zero
    @State private var circleOpacity = 0.5
    @State private var alertActive = false

    var body: some View {
            ZStack {
                Color.green.opacity(0.4)
                    .edgesIgnoringSafeArea(.all)
                
                Circle()
                    .fill(circleColors[colorSelector])
                    .opacity(circleOpacity)
                    .frame(width: ballSize.width, height: ballSize.height)
                    .offset(ballOffset)

                GeometryReader { geometry in
                    VStack(spacing:10) {
                        Spacer()

                        Slider(value: $circleOpacity)
                        
                        HStack {
                            ForEach(0..<buttons1.count,id:\.self){ text in
                                Button(action: {
                                    switch text{
                                    case 0:
                                        colorSelector = 0
                                        ballOffset = .zero
                                    case 1:
                                        handleMove(incrementY: -3, geometryProxy: geometry)
                                    case 2:
                                        colorSelector = colorSelector == (circleColors.count - 1) ? 0 : colorSelector + 1
                                    default:
                                        break
                                    }

                                }, label: {
                                    Text(buttons1[text])
                                        .foregroundColor(text == 1 ? Color.white:Color.accentColor)
                                })
                                .buttonModifier()
                                .background(RoundedRectangle(cornerRadius: 10).fill(Color.accentColor).opacity(text == 1 ? 1 : 0))

                            }
                        }.padding(.horizontal,5)

                        HStack{
                            ForEach(0..<buttons2.count,id:\.self){text in
                                Button(action:{
                                    switch text{
                                    case 0:
                                        handleMove(incrementX: -3, geometryProxy: geometry)
                                    case 1:
                                        handleMove(incrementY: 3, geometryProxy: geometry)
                                    case 2:
                                        handleMove(incrementX: 3, geometryProxy: geometry)
                                    default:break
                                    }
                                },
                                label:{
                                    Text(buttons2[text])
                                        .foregroundColor(Color.white)
                                })
                                .buttonModifier()
                                .background(RoundedRectangle(cornerRadius: 10).fill(Color.blue))
                            }
                        }.padding([.bottom,.horizontal],5)

                    }
                    .background(Color.orange.opacity(0.5))
                }

                .alert(isPresented: $alertActive, content: {
                    Alert(title: Text("out of bounds"), message: Text("out"), dismissButton: .default(Text("OK")))
                })
            }

    }
    
    
    func handleMove(incrementX: CGFloat = 0, incrementY: CGFloat = 0, geometryProxy: GeometryProxy) {

        let newOffset = CGSize(width: ballOffset.width + incrementX, height: ballOffset.height + incrementY)
        
        let standardXOffset =
            (geometryProxy.size.width) / 2 // width (excluding safe area)
            - (ballSize.width / 2) // subtract 1/2 of ball width (anchor is in the center)
        let maxXOffset = standardXOffset + geometryProxy.safeAreaInsets.trailing // + trailing safe area
        let minXOffset = -standardXOffset - geometryProxy.safeAreaInsets.leading // + leading safe area

        let standardYOffset =
            (geometryProxy.size.height / 2) // height (excluding safe area)
            - (ballSize.height / 2) // subtract 1/2 of ball height (anchor is in the center)
        let maxYOffset = standardYOffset + geometryProxy.safeAreaInsets.bottom // + bottom safe area
        let minYOffset = -standardYOffset - geometryProxy.safeAreaInsets.top // + top safe area
        
        if
            (newOffset.width >= maxXOffset) ||
            (newOffset.width <= minXOffset) ||
            (newOffset.height >= maxYOffset) ||
            (newOffset.height <= minYOffset) {
            alertActive.toggle()
        } else {
            ballOffset = newOffset
        }
    }
    
}