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
}
}
}
我正在尝试将这个圆移到屏幕的边缘,然后确定它是否在屏幕的边界并显示警报。
这是我已经尝试过的方法:尝试过 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
}
}
}