I am making a custom label for a text input.
The idea is when the text input is changed, the label changes with it.
I can get it to work, as long as i load the view with the id value with some value in it, such as 1234
But if i have it blank...
Also, it only stores those values, it wont change when you type in the text input.
the EFFECT works, just not the numbers.
VStack {
Custom_Input(id: "")
}
it throws the following error.
Thread 1: Fatal error: String index is out of bounds
This works fine
VStack {
Custom_Input(id: "1234")
}
im not sure how to approach this.
This is the entire view here.
import SwiftUI
struct Custom_Input: View {
#State var id: String = " "
#State var label1: Character = Character(" ")
#State var label2: Character = Character(" ")
#State var label3: Character = Character(" ")
#State var label4: Character = Character(" ")
var body: some View {
ZStack {
Color.green
VStack {
HStack(spacing: 13){
if(id.count < 1) {
Label("0", systemImage: "")
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.black)
} else {
Label(String(label1), systemImage: "")
.foregroundColor(.white)
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.white)
}
if(id.count < 2) {
Label("0", systemImage: "")
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.black)
} else {
Label(String(label2), systemImage: "")
.foregroundColor(.white)
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.white)
}
if(id.count < 3) {
Label("0", systemImage: "")
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.black)
} else {
Label(String(label3), systemImage: "")
.foregroundColor(.white)
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.white)
}
if(id.count < 4) {
Label("0", systemImage: "")
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.black)
} else {
Label(String(label4), systemImage: "")
.foregroundColor(.white)
.frame(height: 48)
.padding(.leading, -7)
.foregroundColor(.white)
}
}
.onAppear {
label1 = id[0]
label2 = id[1]
label3 = id[2]
label4 = id[3]
}
.frame(width: 311, height: 48)
TextField("", text: $id)
.background(.red)
.frame(width: 311, height: 48)
}
}
.frame(width: 311, height: 48)
}
}
extension StringProtocol {
subscript(offset: Int) -> Character {
self[index(startIndex, offsetBy: offset)]
}
}
struct Custom_Input_Previews: PreviewProvider {
static var previews: some View {
Custom_Input(id: "1234")
}
}
A lot of this code is duplicated, and can be simplified.
Firstly, why didn't your solution work, and why did it cause a runtime error? Well, when you initialized it with a string with less than 4 characters, you are accessing the String in an out-of-bounds index. The onAppear(perform:) is run as soon as the view is visible, so this is where the index is accessed causing the error.
To fix this, you can just get the character when you need it / know it exists. In my solution below, I created an extension to safely get the index, otherwise it returns nil:
extension StringProtocol {
subscript(safe offset: Int) -> Character? {
guard 0 ..< count ~= offset else {
return nil
}
return self[index(startIndex, offsetBy: offset)]
}
}
The main view code has been simplified with a ForEach so code doesn't have to be repeated. The layout is also almost identical, but greatly simplified. Code:
struct CustomInput: View {
#Binding var id: String
var body: some View {
VStack(spacing: 20) {
HStack(spacing: 13) {
ForEach(0 ..< 4) { index in
Text(String(id[safe: index] ?? "0"))
.foregroundColor(id.count <= index ? .black : .white)
}
}
TextField("", text: $id)
.background(.red)
}
.padding(.vertical)
.background(Color.green)
.padding(30)
}
}
Usage:
struct ContentView: View {
#State private var id = ""
var body: some View {
CustomInput(id: $id)
}
}
Result:
Related
I was trying to make resizable header but it breaks.
I am trying to clone twitter profile,
I think logic is right but can I know why this one is not working?
I made HStack and try to hide it but when I scroll back it can't come back.
Tried with GeometryReader
Please help me, thanks
import SwiftUI
struct ProfileView: View {
// change views
#State var isHide = false
#State private var selectionFilter: TweetFilterViewModel = .tweets
#Namespace var animation
var body: some View {
VStack(alignment: .leading) {
//hiding
if isHide == false {
headerView
actionButtons
userInfoDetails
}
tweetFilterBar
.padding(0)
ScrollView(showsIndicators: false) {
LazyVStack {
GeometryReader { reader -> AnyView in
let yAxis = reader.frame(in: .global).minY
let height = UIScreen.main.bounds.height / 2
if yAxis < height && !isHide {
DispatchQueue.main.async {
withAnimation {
isHide = true
}
}
}
if yAxis > 0 && isHide {
DispatchQueue.main.async {
withAnimation {
isHide = false
}
}
}
return AnyView(
Text("")
.frame(width: 0, height: 0)
)
}
.frame(width: 0, height: 0)
ForEach(0...9, id: \.self) { _ in
TweetRowView()
}
}
}
Spacer()
}
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView()
}
}
extension ProfileView {
var headerView: some View {
ZStack(alignment: .bottomLeading) {
Color(.systemBlue)
.ignoresSafeArea()
VStack {
Button {
} label: {
Image(systemName: "arrow.left")
.resizable()
.frame(width: 20, height: 16)
.foregroundColor(.white)
.position(x: 30, y: 12)
}
}
Circle()
.frame(width: 72, height: 72)
.offset(x: 16, y: 24)
}.frame(height: 96)
}
var actionButtons: some View {
HStack(spacing: 12){
Spacer()
Image(systemName: "bell.badge")
.font(.title3)
.padding(6)
.overlay(Circle().stroke(Color.gray, lineWidth: 0.75))
Button {
} label: {
Text("Edit Profile")
.font(.subheadline).bold()
.accentColor(.black)
.padding(10)
.overlay(RoundedRectangle(cornerRadius: 20).stroke(Color.gray, lineWidth: 0.75))
}
}
.padding(.trailing)
}
var userInfoDetails: some View {
VStack(alignment: .leading) {
HStack(spacing: 4) {
Text("Heath Legdet")
.font(.title2).bold()
Image(systemName: "checkmark.seal.fill")
.foregroundColor(Color(.systemBlue))
}
.padding(.bottom, 2)
Text("#joker")
.font(.subheadline)
.foregroundColor(.gray)
Text("Your mom`s favorite villain")
.font(.subheadline)
.padding(.vertical)
HStack(spacing: 24) {
Label("Gothem.NY", systemImage: "mappin.and.ellipse")
Label("www.thejoker.com", systemImage: "link")
}
.font(.caption)
.foregroundColor(.gray)
HStack(spacing: 24) {
HStack {
Text("807")
.font(.subheadline)
.bold()
Text("following")
.font(.caption)
.foregroundColor(.gray)
}
HStack {
Text("200")
.font(.subheadline)
.bold()
Text("followers")
.font(.caption)
.foregroundColor(.gray)
}
}
.padding(.vertical)
}
.padding(.horizontal)
}
var tweetFilterBar: some View {
HStack {
ForEach(TweetFilterViewModel.allCases, id: \.rawValue) { item in
VStack {
Text(item.title)
.font(.subheadline)
.fontWeight(selectionFilter == item ? .semibold : .regular)
.foregroundColor(selectionFilter == item ? .black : .gray)
ZStack {
Capsule()
.fill(Color(.clear))
.frame(height: 3)
if selectionFilter == item {
Capsule()
.fill(Color(.systemBlue))
.frame(height: 3)
.matchedGeometryEffect(id: "filter", in: animation)
}
}
}
.onTapGesture {
withAnimation(.easeInOut) {
self.selectionFilter = item
}
}
}
}
}
}
While the animation between show and hide is running, the GeometryReader is still calculating values – which lets the view jump between show and hide – and gridlock.
You can introduce a new #State var isInTransition = false that checks if a show/hide animation is in progress and check for that. You set it to true at the beginning of the animation and to false 0.5 secs later.
Also I believe the switch height is not exactly 1/2 of the screen size.
So add a new state var:
#State var isInTransition = false
and in GeometryReader add:
GeometryReader { reader -> AnyView in
let yAxis = reader.frame(in: .global).minY
let height = UIScreen.main.bounds.height / 2
if yAxis < 350 && !isHide && !isInTransition {
DispatchQueue.main.async {
isInTransition = true
withAnimation {
isHide = true
}
}
// wait for animation to finish
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isInTransition = false
}
} else if yAxis > 0 && isHide && !isInTransition {
DispatchQueue.main.async {
isInTransition = true
withAnimation {
isHide = false
}
}
// wait for animation to finish
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isInTransition = false
}
}
return AnyView(
// displaying values for test purpose
Text("\(yAxis) - \(isInTransition ? "true" : "false")").foregroundColor(.red)
// .frame(width: 0, height: 0)
)
}
I have forms in my SwiftUI app. In some of them there is a problem relating to keyboard's return key. After editing the form, when I tap the return key to resign the keyboard it erases all the edited data in the form. I could not find any reasonable cause of this problem. I have many network calls in the app.
Here is the code of the login form:
import SwiftUI
struct FormView: View {
var size: CGSize
#State private var errorMessage: String = ""
#State private var isConnectionFailed: Bool = false
#State private var isLoginActive: Bool = false
#ObservedObject var viewModel = LoginViewModel()
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
ZStack {
VStack {
VStack {
Image("Logo Registration")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 150)
Text("Welcome")
.bold()
.font(.system(size: 22))
.foregroundColor(Color("T1"))
.padding(.top)
Text("Sign in to continue")
.font(.system(size: 18))
.foregroundColor(Color("T1"))
.padding(.top, 8)
}
.padding()
.padding(.bottom)
VStack(spacing: 20) {
HStack {
Image("Call Us_Menu")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 35)
.padding(.leading, -10)
Divider()
.rotationEffect(Angle(degrees: 180))
.frame(height: 50)
Text("+88")
.foregroundColor(.gray)
.padding(.horizontal, 10)
Divider()
.rotationEffect(Angle(degrees: 180))
.frame(height: 50)
TextField("Mobile Number", text: self.$viewModel.mobileNumber)
}
.crTextFieldStyle(size: size, height: 50)
HStack {
Image("Password")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 35)
.padding(.leading, -10)
Divider()
.rotationEffect(Angle(degrees: 180))
.frame(height: 50)
SecureField("Password", text: self.$viewModel.password)
}
.crTextFieldStyle(size: size, height: 50)
}
HStack {
Text(self.errorMessage)
.font(.system(size: 12))
.foregroundColor(Color("T2"))
Spacer()
}
.frame(width: size.width/1.2)
HStack {
NavigationLink(
destination: ResetPasswordStepOneView()
.navigationBarHidden(isHidden: true)
) {
Text("Forgot Password?")
.foregroundColor(Color("T1"))
}
Spacer()
}
.padding(.bottom, 40)
.frame(width: size.width/1.2)
.font(.system(size: 14))
if self.viewModel.loginModel?.status == "ok" {
NavigationLink(
destination: ContentView()
.navigationBarHidden(isHidden: true)
.navigationBarBackButtonHidden(true),
isActive: $isLoginActive,
label: {
EmptyView()
}
)
}
Button(action: {
if self.viewModel.mobileNumber == "" || self.viewModel.password == "" {
self.errorMessage = "Mobile number or password is missing"
} else if Connectivity.isConnectedToInternet() {
self.viewModel.loading = true
self.errorMessage = ""
self.isLoginActive.toggle()
self.viewModel.fetchWithAF()
} else {
self.isConnectionFailed = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.isConnectionFailed = false
}
}
}) {
Text("SIGN IN")
}
.frame(width: size.width/1.2, height: 50)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color("B7"), lineWidth: 1)
)
.foregroundColor(Color("T2"))
Text("Or")
.padding()
NavigationLink(
destination: RegistrationView()
.navigationBarHidden(isHidden: true)
) {
Text("CREATE NEW ACCOUNT")
.foregroundColor(Color("T2"))
.bold()
}
Spacer()
}
.frame(width: size.width)
.padding(.bottom, 170)
if self.isConnectionFailed {
ConnectivityError()
}
}
}
}
}
LoginViewModel:
import Foundation
import SwiftUI
import Alamofire
class LoginViewModel: ObservableObject{
#Published var loginModel: LoginModel?
#Published var loading: Bool = false
#Published var isError: Bool = false
#Published var mobileNumber: String = ""
#Published var password: String = ""
#Published var isActive = false
func fetchWithAF() {
let registrationReq = LoginReqModel(phone: "+88" + self.mobileNumber, password: self.password)
let url = AppConstant.signin
let headers: HTTPHeaders = [
"Content-Type": "application/json"
]
AF.request(URL.init(string: url)!, method: .post, parameters: registrationReq, encoder: JSONParameterEncoder.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success(_):
DispatchQueue.main.async {
do {
self.loginModel = try JSONDecoder().decode(LoginModel.self, from: response.data!)
if self.loginModel?.payload?.count ?? 0 > 0 {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(self.loginModel) {
let defaults = UserDefaults.standard
defaults.set(encoded, forKey: AppConstant.loginPayoad)
}
AppConstant.token = self.loginModel?.payload![0].accessToken ?? ""
}
self.loading = false
if self.loginModel?.status == "ok" {
self.isActive = true
UserDefaults.standard.set(self.mobileNumber, forKey: "Mobile")
UserDefaults.standard.set(self.password, forKey: "Password")
}
if self.loginModel?.status == "error" {
self.isError = true
}
} catch {
print("")
}
}
break
case .failure(let error):
print("working error \(error)")
break
}
}
}
}
Your viewModel is recreated every time the view reloads. Try changing it to:
#StateObject var viewModel = LoginViewModel()
StateObject (introduced in iOS 14) will ensure the object is only created once per view. ObservedObjects need to be owned by some parent view or class.
I have a My Profile form in my app. In the form I have some buttons. Among them 5 buttons are used to present custom drop down menus to select Sax, Blood, Nationality, and so on. However, when I tap these buttons sometime they response some time don't. For example, if I tap them 10 time they response 2 or 3 times. Some time they work for long tap. I have spent days on this matter, but could not identify any problem in my code. Is there anyone who faced this sort of problems? Could anyone figure out what is the actual problem. By the way, all other buttons in my app working absolutely fine.
This is the code for first 3 buttons in red rectangle in the picture
import SwiftUI
struct FormPartTwoView: View {
#Binding var gender: String
#Binding var blood: String
#Binding var nationality: String
#Binding var showingGenderPicker: Bool
#Binding var showingBloodGroupPicker: Bool
#Binding var showingNationalityPicker: Bool
#ObservedObject var userProfile = UserProfileViewModel()
#ObservedObject var userProfileUpdate = UserProfileUpdateViewModel()
var body: some View {
GeometryReader { geometry in
HStack {
Button(action: {
self.showingGenderPicker.toggle()
self.showingBloodGroupPicker = false
self.showingNationalityPicker = false
}) {
VStack {
TextField("Gender", text: self.$gender)
.padding(.horizontal)
.disabled(true)
}
.font(.system(size: 13))
.frame(height: 40)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color("Border2"), lineWidth: 1)
)
}
.buttonStyle(PlainButtonStyle())
Button(action: {
self.showingBloodGroupPicker.toggle()
self.showingGenderPicker = false
self.showingNationalityPicker = false
}) {
VStack {
TextField("Blood", text: self.$blood)
.padding(.horizontal)
.disabled(true)
}
.font(.system(size: 13))
.frame(height: 40)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color("Border2"), lineWidth: 1)
)
}
.buttonStyle(PlainButtonStyle())
Button(action: {
self.showingNationalityPicker.toggle()
self.showingGenderPicker = false
self.showingBloodGroupPicker = false
}) {
VStack {
TextField("Nationality", text: self.$nationality)
.padding(.horizontal)
.disabled(true)
}
.font(.system(size: 13))
.frame(height: 40)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color("Border2"), lineWidth: 1)
)
}
.buttonStyle(PlainButtonStyle())
}
.frame(width: geometry.size.width, height: 40)
}
}
}
struct FormPartTwoView_Previews: PreviewProvider {
static var previews: some View {
FormPartTwoView(gender: .constant(""), blood: .constant(""), nationality: .constant(""), showingGenderPicker: .constant(false), showingBloodGroupPicker: .constant(false), showingNationalityPicker: .constant(false))
}
}
This is the code for second tow Buttons:
import SwiftUI
struct FormPartFiveView: View {
#Binding var district: String
#Binding var upazila: String
#Binding var postcode: String
#Binding var showingUpazilaPicker: Bool
#Binding var showingDistrictPicker: Bool
#ObservedObject var userProfileUpdate = UserProfileUpdateViewModel()
var body: some View {
GeometryReader { geometry in
HStack {
Button(action: {
self.showingDistrictPicker.toggle()
self.showingUpazilaPicker = false
}) {
VStack {
Text("\(self.district == "" ? "District" : self.district)")
.foregroundColor(self.district == "" ? Color.gray : Color.black)
.padding(.horizontal)
}
.font(.system(size: 13))
.frame(width: geometry.size.width/3.2, height: 40)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color("Border2"), lineWidth: 1)
)
}
.buttonStyle(PlainButtonStyle())
Button(action: {
self.showingUpazilaPicker.toggle()
self.showingDistrictPicker = false
}) {
VStack {
Text("\(self.upazila == "" ? "Upazila" : self.upazila)")
.foregroundColor(self.upazila == "" ? Color.gray : Color.black)
.padding(.horizontal)
}
.font(.system(size: 13))
.frame(width: geometry.size.width/3.2, height: 40)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color("Border2"), lineWidth: 1)
)
}
.buttonStyle(PlainButtonStyle())
VStack {
TextField("Postcode", text: self.$postcode)
.padding(.horizontal)
}
.font(.system(size: 13))
.frame(width: 100, height: 40)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color("Border2"), lineWidth: 1)
)
}
.frame(width: geometry.size.width, height: 40)
}
}
}
struct FormPartFiveView_Previews: PreviewProvider {
static var previews: some View {
FormPartFiveView(district: .constant(""), upazila: .constant(""), postcode: .constant(""), showingUpazilaPicker: .constant(false), showingDistrictPicker: .constant(false))
}
}
I'm working on a checklist app that has several arrays with checks. I'd like to save the state if a users closes/quits the app. I was thinking of using the UserDefault methods for this:
HStack {
ForEach(0 ..< checklist.steps) { index in
VStack {
Button(action: {
self.checked[index].toggle()
UserDefaults.standard.set(self.checked[index], forKey: "Check")
I'm currently using the following state for checks:
#State private var checked = [false, false, false, false, false, false]
Does anyone know how to apply UserDefaults for arrays or generally how to save the state for your app when closing it?
Thanks in advance!
try this: (it is the code from last time ;)) i think the true/false settings itself is not correct, but the saving / loading works ;)
struct ChecklistView: View {
var checklist: Checklist
#State var currentProgress: Float = 0.0
#State var checked : [Bool] = [false, false, false, false]
init(checklist: Checklist) {
self.checklist = checklist
}
func saveUserDefaults() {
var value = ""
for eachValue in checked {
if eachValue == true { value += "1" }
else { value += "0" }
}
UserDefaults.standard.set(value, forKey: checklist.title)
}
var body: some View {
ZStack(alignment: .leading) {
// RoundedRectangle(cornerRadius: 20)
// .foregroundColor(.red).opacity(0.5)
// .frame(width: 200)
VStack {
HStack(alignment: .top) {
checklist.image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
.padding(.trailing)
VStack(alignment: .leading) {
Text(checklist.title)
.font(.system(size: 16, weight: .medium))
.padding(.bottom, 4)
Text(checklist.instruction.uppercased()).font(.system(size: 12))
HStack {
ForEach(0 ..< checklist.steps) { index in
VStack {
Button(action: {
self.checked[index].toggle()
print(self.checked)
self.saveUserDefaults()
}) {
ZStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(self.checked[index] ? Color("LightGreen") : .gray )
.frame(width: 40, height: 40)
Image(systemName: self.checked[index] ? "checkmark" : "plus")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 16, height: 16)
.foregroundColor(self.checked[index] ? .white : .white)
}
}
}
}
}
}
}.frame(width: 350, alignment: .leading)
}
}
.onAppear() {
if let values = UserDefaults.standard.value(forKey: self.checklist.title) as? String {
self.checked = values.map {
if $0 == "1" { return true }
return false
}
}
print(self.checked)
}
.frame(width: 350)
.padding(.bottom, 16)
.cornerRadius(8)
.padding(.top, 20)
}
}
I haven't test that solution, but can't you store your array in your NSUserdefaults after every button action?
Button(action: {
self.checked[index].toggle()
UserDefaults.standard.set(self.checked, forKey: "Check")
Probably, the best solution would be using CoreData for that, which would be very easier to use.
PS: I can test that solution later on.. not at my Mac right now
I'm trying to create in SwiftUI an action sheet that appears after pressing a button and allow the user to select and return an item throught a picker (like this https://imgur.com/a/IbS7swX).
Any hint on how to do it?
Thanks
struct ContentView: View {
init() {
UITableView.appearance().separatorColor = .clear
}
var inputArray = ["100","101","102"]
#State var slectedSegmant = "ActionSheet"
#State var slectedObj = "101"
#State var enableSheet = false
var test = false
var body: some View {
ZStack {
VStack(spacing: 10) {
Picker(selection: $slectedSegmant, label: Text("Segment")) {
Text("Alert").tag("Alert")
Text("ActionSheet").tag("ActionSheet")
}.pickerStyle(SegmentedPickerStyle())
.labelsHidden()
.padding(EdgeInsets.init(top: 50, leading: 10, bottom: 0, trailing: 10))
Text("Alert & Pickers")
.font(Font.system(size: 35, design: .rounded))
.fontWeight(.bold)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
List((0...50),id: \.self) { input in
ZStack {
HStack(spacing: 10) {
Image(systemName: "book")
.font(.title)
.padding(.leading, 10)
VStack(alignment: .leading, spacing: 5, content: {
Text("Simple")
Text("3 different buttons")
})
Spacer()
}.padding(.vertical)
.frame(maxWidth:.infinity)
.background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.white).shadow(radius: 1.5)
)
Button(action: {
self.enableSheet = true
}) {
Text("")
}
}
}.padding()
}.blur(radius: $enableSheet.wrappedValue ? 1 : 0)
.overlay(
$enableSheet.wrappedValue ? Color.black.opacity(0.6) : nil
)
if $enableSheet.wrappedValue {
GeometryReader { gr in
VStack {
VStack {
Text("PickerView")
.font(.headline)
.foregroundColor(.gray)
.padding(.top, 10)
Text("Prefered ContentHeight")
.padding(.top, 5)
Picker("test", selection: self.$slectedObj) {
Text("100").id("100")
Text("101").id("101")
Text("101").id("102")
}.labelsHidden()
}.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color.white).shadow(radius: 1))
VStack {
Button(action: {
debugPrint("Done Selected")
self.enableSheet = false
}) {
Text("Done").fontWeight(Font.Weight.bold)
}.padding()
.frame(maxWidth: gr.size.width - 90)
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color.white).shadow(radius: 1))
}
}.position(x: gr.size.width / 2 ,y: gr.size.height - 200)
}
}
}.edgesIgnoringSafeArea(.all)
}
}
OUTPUT