Loading Animation is falling from top - swiftui

im using this example to build a loading animation, and animated part is just falling from the sky.
struct ContentView: View {
#State private var isLoading = false
var body: some View {
ZStack {
Text("Loading")
.font(.system(.body, design: .rounded))
.bold()
.offset(x: 0, y: -25)
RoundedRectangle(cornerRadius: 3)
.stroke(Color(.systemGray5), lineWidth: 3)
.frame(width: 250, height: 3)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.green, lineWidth: 3)
.frame(width: 30, height: 3)
.offset(x: isLoading ? 110 : -110, y: 0)
.animation(Animation.linear(duration: 1).repeatForever(autoreverses: false))
}
.onAppear() {
self.isLoading = true
}
}
}

You have a couple of problems with your code. First, .animation() has been deprecated because it is unreliable. You should be using .animation(_:value:) instead.
While I can't duplicate the "falling from the top" problem, I think I know what it is. It is an animation timing issue when the animation is triggered before the view is on screen. If you continue to get this, wrap your change in .onAppear() in DispatchQueue.main.asyncAfter(deadline:). The code with this is below:
struct ContentView: View {
#State private var isLoading = false
var body: some View {
ZStack {
Text("Loading")
.font(.system(.body, design: .rounded))
.bold()
.offset(x: 0, y: -25)
RoundedRectangle(cornerRadius: 3)
.stroke(Color(.systemGray5), lineWidth: 3)
.frame(width: 250, height: 3)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.green, lineWidth: 3)
.frame(width: 30, height: 3)
.offset(x: isLoading ? 110 : -110, y: 0)
// Use this init for .animation, as the other one is depracated
.animation(Animation.linear(duration: 1).repeatForever(autoreverses: false), value: isLoading)
}
.onAppear() {
// This may not be necessary, but if you get the "falling from the top" problem, put this
// around isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
isLoading = true
}
}
}
}

Can you try 3 things:
Remove the instances of offset coordinates where value = 0. Like x=0 y=0 etc
Add stop animation inside onDisappear()
.onDisappear() {
self.isLoading = false
}
If you are using .transition() in this or any of its parent view, try removing that

Related

Do not want NavigationView to popup after some tap of the button

I am creating an app to give reward stickers for kids. When 10 stickers are collected, I want all stickers are removed from the main view and the image will be shown before removing everything. This image is shown at the 11th click of the button but navigation view to select stickers are also shown on top of this image, which I do not want to be popped up.
Here is my code...
NavigationView {
ZStack {
Image("Greenbase")
.resizable()
.frame(width: self.screenWidth, height: self.screenWidth)
ZStack {
ForEach($stickers) { $sticker in
VStack {
Image(sticker.name)
.resizable()
.frame(width: self.screenWidth*0.2, height: self.screenWidth*0.2)
.offset(x: self.selectedSticker == sticker ? sticker.offset.width + self.dragOffset.width : sticker.offset.width,
y: self.selectedSticker == sticker ? sticker.offset.height + self.dragOffset.height : sticker.offset.height)
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { (value, state, transaction) in
if nil == self.selectedSticker {
self.selectedSticker = sticker
}
state = value.translation
})
.onEnded { value in
sticker.offset.height += value.translation.height + dragOffset.height
sticker.offset.width += value.translation.width + dragOffset.width
self.selectedSticker = nil
}
)
}//vstack
.sheet(isPresented: $isAddingSticker, onDismiss: addSticker) {
StampView(sticker: $selectedSticker)
}//foreach
if counter < 11 {
Button {
isAddingSticker = true
print("button pressed: \(counter)")
counter += 1
}label: {
RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color(red: 55/255, green: 266/255, blue: 213/255))
.frame(width: 300, height: 40, alignment: .center)
.overlay(
Text(" ステッカーをはろう!")
.foregroundColor(Color(red: 41/255, green: 52/255, blue: 98/255))
.font(.title2))
}//button label
}else {
Button{
isAddingSticker = false
counter = 0
removeSticker(at: 1)
}label: {
Image("WellDone")
.resizable()
.frame(width: self.screenWidth*0.5, height: self.screenWidth*0.5)
.scaleEffect(2.0)
.rotationEffect(Angle(degrees: 360 ))
.animation(.easeInOut(duration: 3.0))
}//button label
} //else
}//Hstack
.disabled(isEditing)
}
}//zstack stamps
}//zstac w bc image
}//body
func removeSticker(at index: Int) {
self.stickers.removeAll()//< -- Here
}
func addSticker() {
guard let name = selectedSticker else { return }
withAnimation {
stickers.insert(name, at: 0)
}
}
}//struct
I am still super new to Swift... the question should be very basic but hopefully can find the answer.
I don't think I understand the question correctly, but you can hide the navigation with this modifier at the end of the navigation view.
NavigationView {
...
}.navigationBarHidden(true)

SwiftUI: Adding a Shadow to View in List/Grid is Lagging UI

When adding a shadow to my view in a Grid, the scrolling experience is bad. It feels like the Frame Rate is dropping. I came across this post, option 1 made my whole background for my view the same color as my shadow. I Don't really know how to implement UIViewRepresentable in option 2.
So how would I be able to use UIViewRepresentable, or is there a better way to do this.
MRE CODE
struct ContentView: View {
#State var gridSpacing: CGFloat = 8
let columns: [GridItem] = [GridItem(.flexible(),spacing: 8), GridItem(.flexible(),spacing: 8),GridItem(.flexible(),spacing: 8)]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: gridSpacing) {
ForEach(0..<200) { x in
VStack(spacing: 8) {
Image("sumo-deadlift") //<----- Replace Image
.resizable()
.scaledToFit()
.background(.yellow)
.clipShape(Circle())
.padding(.horizontal)
.shadow(color: .blue, radius: 2, x: 1, y: 1) //<----- Comment/Uncomment
Text("Sumo Deadlift")
.font(.footnote.weight(.semibold))
.multilineTextAlignment(.center)
.lineLimit(2)
.frame(maxWidth: .infinity)
Text("Legs")
.font(.caption)
.foregroundStyle(.secondary)
}
.padding(.all)
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
.mask(RoundedRectangle(cornerRadius: 20))
.shadow(color: .red, radius: 2, x: 1, y: 1) //<----- Comment/Uncomment
}
}
.padding(.all, 8)
}
.background(.ultraThinMaterial)
}
}

At SwiftUI how do you assign different actions if buttons are created in a loop

How can you assign different actions if you are creating buttons in a loop when using SwiftUI.
I was using sender tag while using UIView.
At following code, every buttons calls the same function as usual.
How can I make them call different action in case we are using loops to create buttons.
var buttonNames = ["OK", "NOPE"]
var body: some View {
let width = UIScreen.main.bounds.width / CGFloat(buttonNames.count)
ScrollView (.horizontal, showsIndicators: false) {
LazyHStack {
ForEach(0..<buttonNames.count, id: \.self) { index in
Button(buttonNames[index]) {
buttonTouched()
}.frame(width: width)
.background(Color.gray)
}
}
.frame(width: UIScreen.main.bounds.width, height: 45, alignment: .center)
.foregroundColor(.white)
.background(Color.gray)
}
.position(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height - 45)
}
After the comment I did it like this and it makes the trick.
var buttonNames = ["OK", "NOPE"]
#State var touchedButton = 0
func buttonTouched(){
print("Button tapped! \(touchedButton)")
}
var body: some View {
let width = UIScreen.main.bounds.width / CGFloat(buttonNames.count)
ScrollView (.horizontal, showsIndicators: false) {
LazyHStack {
ForEach(0..<buttonNames.count, id: \.self) {
index in
Button(action:{
touchedButton = index
buttonTouched()
}
){
Text(buttonNames[index])
}
.frame(width: width)
}
}
.frame(width: UIScreen.main.bounds.width, height: 45, alignment: .center)
.foregroundColor(.white)
.background(Color.gray)
} .position(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height - 45)
}

Why the text appear at loading with animation?

I want to hide some text behind the NavigationBarTitle and show it when user press a button, it's working fine. The only problem is with the animation. At loading we see the text moving behind the NavigationBarTitle and that's not what i want.
How can i fix this?
I tried with offset and position but it's not working...
Code :
import SwiftUI
struct TestZStackNavigationView: View {
#State var isShowed = false
let screenSize: CGRect = UIScreen.main.bounds
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 0){
Text("Im the hidden text")
.fontWeight(.bold)
.foregroundColor(Color.white)
.frame(width: screenSize.width, height: 40, alignment: .center)
.background(Color.red)
// .offset(y: self.isShowed ? -315 : -355)
.position(x: screenSize.width / 2, y: self.isShowed ? 20 : -40)
.animation(.easeIn(duration: 0.5))
Button(action: {
self.isShowed.toggle()
}) {
Text("click me")
}
.navigationBarTitle(Text("Navigation Bar Title"), displayMode:.inline)
}
}
}
}
struct TestZStackNavigationView_Previews: PreviewProvider {
static var previews: some View {
TestZStackNavigationView()
}
}
There are two possibilities
make animation per-state (and no other changes)
.animation(.easeIn(duration: 0.5), value: isShowed)
replace implicit animation as modifier and add explicit animation in action
.position(x: screenSize.width / 2, y: self.isShowed ? 20 : -40)
// .animation(.easeIn(duration: 0.5)) // remove from here
Button(action: {
withAnimation(Animation.easeIn(duration: 0.5)) { // << add this
self.isShowed.toggle()
}
}) {
Text("click me")
}
I want to use it now with an ObservedObject but i got that same behavior as first code. Why?
struct TestZStackNavigationView: View {
// #State var isShowed = false
let screenSize: CGRect = UIScreen.main.bounds
#ObservedObject var online = NetStatus()
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 0){
Text("Im the hidden text")
.fontWeight(.bold)
.foregroundColor(Color.white)
.frame(width: screenSize.width, height: 40, alignment: .center)
.background(Color.red)
.position(x: screenSize.width / 2, y: online.connected ? -40 : 20)
.animation(.easeIn(duration: 0.5), value: online.connected)
.navigationBarTitle(Text("Navigation Bar Title"), displayMode:.inline)
}
}
}
}

How to use UserDefault on an Array in SwiftUI

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