Animate Swapping of Objects - animation

I am creating a memory game. In the first sequence all cards flip over and two cards matching cards will highlight. All cards will flip back over and randomly chosen cards will swap positions. I can get the values to swap using the array.swap array.shuffle functions, however I would like to animate the cards themselves swapping. I can get the cards to flip, however switching is another story. I have several other classes that go with this, but stackoverflow won't let me post more. If you need more detail please let me know.
My code:
struct findTheCardsFourCards: View {
#Namespace var cardAnimation: Namespace.ID
#State var flipped = false
#State var animate3d = false
#State var rememberTheCard = false
#State var timeRemaining = 15
#State var selectYourCardsText = false
#State var shuffleCards = false
let showCardsFirst = 15
let startGame = 11
let selectYourCards = 5
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
#EnvironmentObject var game: CardModel
var rowCount = 2
var colCount = 2
var body: some View {
ZStack{
VStack{
ForEach(0 ..< game.cardInGame.count/rowCount){row in
HStack{
ForEach(0 ..< colCount){col in
CardView(card: game.cardInGame[row * rowCount + col], cardAnimation: cardAnimation, flipped: flipped, animate3d: animate3d, action: {game.cardInGame.swapAt(2, 3)})
.shadow(color: Color.purple, radius: highlightCards(card: game.cardInGame[row*rowCount+col]))
.animation(.linear)
.onReceive(timer, perform: { _ in
switch timeRemaining{
case 14:
flipped.toggle()
case 8:
game.cardInGame.swapAt(1,2)
default: break
}
/*
if timeRemaining > startGame {
game.cardInGame[row * rowCount + col].cardSelected = true
}
else if timeRemaining == startGame - 1{
game.cardInGame[row * rowCount + col].cardSelected = false
}
if timeRemaining == startGame {
shuffleCards = true
game.cardInGame.shuffle()
}*/
})
}
}
}
}
HStack{
Text("REMEMBER THE CARDS")
}
.opacity(rememberTheCard ? 1.0:0.0)
HStack{
Text("SELECT YOUR CARDS")
}
.opacity(selectYourCardsText ? 1.0 : 0.0)
//.layoutPriority(rememberTheCard ? 1.0:0.0)
}
.onReceive(timer, perform: { _ in
switch timeRemaining{
case 13:
rememberTheCard = true
game.flipGame.toggle()
case 10:
rememberTheCard = false
game.flipGame.toggle()
case 8:
game.cardInGame.swapAt(1, 3)
case 6:
selectYourCardsText = true
case 3:
selectYourCardsText = false
default:
break
}
/*if timeRemaining == showCardsFirst {
rememberTheCard = true
game.flipGame.toggle()
}
if timeRemaining == startGame {
rememberTheCard = false
game.flipGame.toggle()
}
if timeRemaining == startGame - 2 {
selectYourCardsText = true
}
if timeRemaining == selectYourCards{
selectYourCardsText = false
}*/
timeRemaining -= 1
})
}
func highlightCards(card: CardItem) -> CGFloat {
//if game.cardInGame[row*rowCount+col].winningCard == true{
if card.winningCard == true {
if game.flipGame == true{
return 10}}
if card.cardSelected == true{
return 10}
return 0
}
}
struct findTheCardsFourCards_Previews: PreviewProvider {
static var previews: some View {
findTheCardsFourCards()
.environmentObject(testCardModel)
.previewDevice("iPad Pro (9.7-inch)")
}
}
struct ContentView: View {
#EnvironmentObject var game: CardModel
#State private var timeRemaining = 5
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
VStack{
findTheCardsFourCards()
HStack{
Button(action: {game.flipGame.toggle()
} ) {
Text("Start Game")
}
}
.padding()
.padding()
.background(Color.green)
.cornerRadius(15)
.padding()
//.onReceive(timer){ time in
//if self.timeRemaining == 0 {
// self.timeRemaining -= 1
//}
//else{
// game.flipGame.toggle()
// }
//}
}
}
}

Related

Is it possible to create 5 oscillators (an array?) working simultaneously and control their frequency and amplitude independently of each other?

import SwiftUI
import AudioKit
class ToneGenerator {
let engine = AudioEngine()
let osc = PlaygroundOscillator()
init(){
engine.output = osc
try! engine.start()
}
}
struct ContentView: View {
let toneGenerator = ToneGenerator()
var freq01: Float = 200
var volume01: Float = 0.5
#State var isPressed = false
var body: some View {
Text("BEEP")
.font((.title))
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged({ _ in
isPressed = true
toneGenerator.osc.frequency = freq01
toneGenerator.osc.amplitude = volume01
toneGenerator.osc.start()
})
.onEnded({ _ in
isPressed = false
toneGenerator.osc.stop()
})
)
}
}
I tried to create several generators, but did not understand how to do it.
All the materials I found on the Internet are related to the Playground and do not work in Xcode.
I'm not 100% sure what you're trying to achieve, but how about something like this. The key is to use $toneGenerators[index] to create a binding to the tone generator so you can change the volume and frequency.
class ToneGenerator: Identifiable {
let id = UUID()
let engine = AudioEngine()
var osc = PlaygroundOscillator()
init(){
engine.output = osc
try! engine.start()
}
}
struct ContentView: View {
#State var toneGenerators: [ToneGenerator] = []
let freqs = [Float(100), 200, 300, 400, 500]
var body: some View {
VStack {
HStack {
Button("BEEP") {
let toneGenerator = ToneGenerator()
toneGenerator.osc.frequency = Float.random(in: 100...1000)
toneGenerator.osc.amplitude = 0.5
toneGenerator.osc.start()
toneGenerators.append(toneGenerator)
}
.font((.title))
Button("STOP") {
toneGenerators.forEach { toneGenerator in
toneGenerator.osc.stop()
}
toneGenerators = []
}
.tint(.red)
.font((.title))
}
Text(toneGenerators.count, format: .number) + Text(" generators")
Grid {
GridRow {
Text("Freq").bold()
Text("Volume").bold()
}
ForEach(toneGenerators) { toneGenerator in
let index = toneGenerators.firstIndex(where: { $0.id == toneGenerator.id })!
GridRow {
Slider(value: $toneGenerators[index].osc.frequency, in: 100...1000)
Slider(value: $toneGenerators[index].osc.amplitude, in: 0...1)
}
}
}
.padding()
Spacer()
}
}
}

SwiftUI animation not continuous after view change

Here is the testing code:
import SwiftUI
struct ContentView: View {
#State private var pad: Bool = false
#State private var showDot: Bool = true
var body: some View {
VStack {
Button {showDot.toggle()} label: {Text("Toggle Show Dot")}
Spacer().frame(height: pad ? 100 : 10)
Circ(showDot: showDot)
Spacer()
}.onAppear {
withAnimation(.linear(duration: 3).repeatForever()) {pad = true}
}
}
}
struct Circ: View {
let showDot: Bool
var body: some View {
Circle().stroke().frame(height: 50).overlay {if showDot {Circle().frame(height: 20)}}
}
}
It happens that after I toggle showDot, the dot circle is not on the center of the stroke circle again! How can I fix this?
The Circ View is given, I can't change that view!
Edit
If you can just hide the view, see solution 1, which is preferred. If you need to re-build the view, see solution 2.
Solution 1
Replace the if condition with a .opacity() modifier that reads 1 when showDot is true.
This way, the dot does not disappear completely, it is there but you just can't see it. You will be toggling the visibility, not the view itself.
Like this:
#State private var pad: Bool = false
#State private var showDot: Bool = true
var body: some View {
VStack {
Button {
showDot.toggle()
} label: {
Text("Toggle Show Dot")
}
Spacer()
.frame(height: pad ? 100 : 10)
Circle().stroke()
.frame(height: 50)
.overlay {
Circle()
.frame(height: 20)
.opacity(showDot ? 1 : 0) // <- Here
}
Spacer()
}
.onAppear {
withAnimation(.linear(duration: 3).repeatForever()) {pad = true}
}
}
Solution 2
You can replace the animation with a timer. Every time it triggers, it will move the whole view by changing the height of the Spacer().
// These variables will track the position and moving direction of the dot
#State private var pos: CGFloat = 0
#State private var movingUp = false
#State private var showDot: Bool = true
// This variable will change the position
// This is a dummy iniatialization, the .onAppear modifier sets the real timer
#State private var timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in }
var body: some View {
VStack {
Button {
showDot.toggle()
} label: {
Text("Toggle Show Dot")
}
Spacer()
.frame(height: pos)
Circle().stroke()
.frame(height: 50)
.overlay {
if showDot {
Circle().frame(height: 20)
}
}
Spacer()
}
.onAppear {
// The timer interval will define the speed
timer = Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { _ in
moveCircle()
}
}
}
private func moveCircle() {
if movingUp {
if pos <= 0 {
pos = 0
movingUp = false
} else {
pos -= 1
}
} else {
if pos >= 100 {
pos = 100
movingUp = true
} else {
pos += 1
}
}
}

Swift UI - animation - terrible performances

I'm trying to display an automatic scrolling text (marquee?) with an animation using Swift UI.
When the mouse is over the text, the animation stops (that's why I store the current state of the animation).
Using one of the latest M1 MBP, this simple animation is using up to 10% of CPU and I'm trying to understand why. Is Swift UI not made for animations like this one or am I doing something wrong? At the end, it's just an animation moving the x offset.
Here is the code of my Marquee.
import SwiftUI
private enum MarqueeState {
case idle
case animating
}
struct GeometryBackground: View {
var body: some View {
GeometryReader { geometry in
Color.clear.preference(key: WidthKey.self, value: geometry.size.width)
}
}
}
struct WidthKey: PreferenceKey {
static var defaultValue = CGFloat(0)
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
typealias Value = CGFloat
}
extension View {
func myOffset(x: CGFloat, y: CGFloat) -> some View {
return modifier(_OffsetEffect(offset: CGSize(width: x, height: y)))
}
func myOffset(_ offset: CGSize) -> some View {
return modifier(_OffsetEffect(offset: offset))
}
}
struct PausableOffsetX: GeometryEffect {
#Binding var currentOffset: CGFloat
#Binding var contentWidth: CGFloat
private var targetOffset: CGFloat = 0.0;
var animatableData: CGFloat {
get { targetOffset }
set { targetOffset = newValue }
}
init(targetOffset: CGFloat, currentOffset: Binding<CGFloat>, contentWidth: Binding<CGFloat>) {
self.targetOffset = targetOffset
self._currentOffset = currentOffset
self._contentWidth = contentWidth
}
public func effectValue(size: CGSize) -> ProjectionTransform {
DispatchQueue.main.async {
self.currentOffset = targetOffset
}
let relativeOffset = targetOffset.truncatingRemainder(dividingBy: contentWidth)
let transform = CGAffineTransform(translationX: relativeOffset, y: 0)
return ProjectionTransform(transform)
}
}
struct Marquee<Content: View> : View {
#State private var isOver: Bool = false
private var content: () -> Content
#State private var state: MarqueeState = .idle
#State private var contentWidth: CGFloat = 0
#State private var isAppear = false
#State private var targetOffsetX: CGFloat = 0
#State private var currentOffsetX: CGFloat
public init(#ViewBuilder content: #escaping () -> Content) {
self.content = content
self.currentOffsetX = 0
}
private func getAnimation() -> Animation {
let duration = contentWidth / 30
print("animation with duration of ", duration)
return Animation.linear(duration: duration).repeatForever(autoreverses: false);
}
public var body : some View {
GeometryReader { proxy in
HStack(alignment: .center, spacing: 0) {
if isAppear {
content()
.overlay(GeometryBackground())
.fixedSize()
content()
.overlay(GeometryBackground())
.fixedSize()
}
}
.modifier(PausableOffsetX(targetOffset: targetOffsetX, currentOffset: $currentOffsetX, contentWidth: $contentWidth))
.onPreferenceChange(WidthKey.self, perform: { value in
if value != self.contentWidth {
self.contentWidth = value
print("Content width = \(value)")
resetAnimation()
}
})
.onAppear {
self.isAppear = true
resetAnimation()
}
.onDisappear {
self.isAppear = false
}
.onHover(perform: { isOver in
self.isOver = isOver
checkAnimation()
})
}
.frame(width: 400)
.clipped()
}
private func getOffsetX() -> CGFloat {
switch self.state {
case .idle:
return self.currentOffsetX
case .animating:
return -self.contentWidth + currentOffsetX
}
}
private func checkAnimation() {
if isOver{
if self.state != .idle {
pauseAnimation()
}
} else {
if self.state != .animating {
resumeAnimation()
}
}
}
private func pauseAnimation() {
withAnimation(.linear(duration: 0)) {
self.state = .idle
self.targetOffsetX = getOffsetX()
}
}
private func resumeAnimation() {
print("Resume animation");
withAnimation(getAnimation()) {
self.state = .animating
self.targetOffsetX = getOffsetX()
}
}
private func resetAnimation() {
withAnimation(.linear(duration: 0)) {
self.currentOffsetX = 0
self.targetOffsetX = 0
self.state = .idle
}
resumeAnimation()
}
}
And we can use it as follow:
Marquee {
Text("Hello, world! Hello, world! Hello, world! Hello, world!").padding().fixedSize()
}.frame(width: 300)
EDIT
I ended up using Core Animation instead of the one built in Swift UI. The cpu / Energy impact is an absolute zero. So I wouldn't recommend using Swift UI animation for long lasting or persistant animations.

Is there a better way to implement a shake animation in swiftui?

I'm trying to get a button to shake when the user tries to log in without filling all the textfields in, and this is what I've come across so far:
struct Shake: GeometryEffect {
var amount: CGFloat = 10
var shakesPerUnit = 3
var animatableData: CGFloat
func effectValue(size: CGSize) -> ProjectionTransform {
ProjectionTransform(CGAffineTransform(translationX:
amount * sin(animatableData * .pi * CGFloat(shakesPerUnit)),
y: 0))
}
}
struct Correct: View {
#State var attempts: Int = 0
var body: some View {
VStack {
Rectangle()
.fill(Color.pink)
.frame(width: 200, height: 100)
.modifier(Shake(animatableData: CGFloat(attempts)))
Spacer()
Button(action: {
withAnimation(.default) {
self.attempts += 1
}
}, label: { Text("Login") })
}
}
}
However, this is particularly useless for a button, and even then the animation seems very off in that its pretty robotic. Can someone suggest an improvement so that I can get my button to shake?
try this
struct ContentView: View {
#State var selected = false
var body: some View {
VStack {
Button(action: {
self.selected.toggle()
}) { selected ? Text("Deselect") : Text("Select") }
Rectangle()
.fill(Color.purple)
.frame(width: 200, height: 200)
.offset(x: selected ? -30 : 0)
.animation(Animation.default.repeatCount(5).speed(6))
}
}
}
I do this to make the field shake and then gets back to it's original position:
private func signUp() {
if email.isEmpty {
withAnimation {
emailIsWrong = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
withAnimation {
emailIsWrong = false
}
}
return
}
}
Where emailIsWrong is a #State variable:
#State private var emailIsWrong = false
Basically after 0.2 sec, I change the emailIsWrong back to false so the view goes back to its position. My text field looks like this:
TextField("Email", text: $email)
.padding()
.frame(height: 45)
.background(Color.white)
.colorScheme(.light)
.offset(x: emailIsWrong ? -8 : 0)
.animation(Animation.default.repeatCount(3, autoreverses: true).speed(6))
A little bit late to the party, but unfortunately the solutions here either finish the animation with a wrong offset or need some hardcoded assumption on the time the animation will finish.
The solution I came up with looks like this:
#State var shake = false
Text("Shake Me")
.font(.title)
.onTapGesture {
shake = true
}
.shake($shake) {
print("Finished")
}
To animate, you just need to set shake to true (it will automatically be set to false once the animation completes).
Here is the implementation:
struct Shake<Content: View>: View {
/// Set to true in order to animate
#Binding var shake: Bool
/// How many times the content will animate back and forth
var repeatCount = 3
/// Duration in seconds
var duration = 0.8
/// Range in pixels to go back and forth
var offsetRange = 10.0
#ViewBuilder let content: Content
var onCompletion: (() -> Void)?
#State private var xOffset = 0.0
var body: some View {
content
.offset(x: xOffset)
.onChange(of: shake) { shouldShake in
guard shouldShake else { return }
Task {
let start = Date()
await animate()
let end = Date()
print(end.timeIntervalSince1970 - start.timeIntervalSince1970)
shake = false
onCompletion?()
}
}
}
// Obs: some of factors must be 1.0.
private func animate() async {
let factor1 = 0.9
let eachDuration = duration * factor1 / CGFloat(repeatCount)
for _ in 0..<repeatCount {
await backAndForthAnimation(duration: eachDuration, offset: offsetRange)
}
let factor2 = 0.1
await animate(duration: duration * factor2) {
xOffset = 0.0
}
}
private func backAndForthAnimation(duration: CGFloat, offset: CGFloat) async {
let halfDuration = duration / 2
await animate(duration: halfDuration) {
self.xOffset = offset
}
await animate(duration: halfDuration) {
self.xOffset = -offset
}
}
}
extension View {
func shake(_ shake: Binding<Bool>,
repeatCount: Int = 3,
duration: CGFloat = 0.8,
offsetRange: CGFloat = 10,
onCompletion: (() -> Void)? = nil) -> some View {
Shake(shake: shake,
repeatCount: repeatCount,
duration: duration,
offsetRange: offsetRange) {
self
} onCompletion: {
onCompletion?()
}
}
func animate(duration: CGFloat, _ execute: #escaping () -> Void) async {
await withCheckedContinuation { continuation in
withAnimation(.linear(duration: duration)) {
execute()
}
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
continuation.resume()
}
}
}
}
here's my one
#State var ringOnFinish: Bool = false
#State var shakeOffset: Double = 0
Button() {
ringOnFinish.toggle()
//give it a little shake animation when off
if !ringOnFinish {
shakeOffset = 5
withAnimation {
shakeOffset = 0
}
} label: {
Image(systemName: "bell\(ringOnFinish ? "" : ".slash")")
.offset(x: ringOnFinish ? 0 : shakeOffset)
.animation(.default.repeatCount(3, autoreverses: true).speed(6), value: ringOnFinish)
}

SwiftUI Passing arguments over to View is not always available

I have a problem with passing arguments over to a View in SwiftUI when calling it. I have this View
import SwiftUI
struct GoodsItemFilterView: View {
#Environment(\.presentationMode) var presentationMode
#State var ref1Array: [String] = []
#State var ref2Array: [String] = []
#State var ref3Array: [String] = []
#State var stockStatusArray: [String] = []
#State var zoneArray: [String] = []
#State var selectorRef1 = 0
#State var selectorRef2 = 0
#State var selectorRef3 = 0
#State var selectorStockStatus = 0
#State var selectorZone = 0
var body: some View {
NavigationView {
Form{
Section(header: Text("Zone"), content: {
Picker(selection: $selectorZone, label:
Text("Zone")) {
ForEach(0 ..< zoneArray.count, id:\.self) {
Text(self.zoneArray[$0])
}
}
})
Section(header: Text("References"), content: {
Picker(selection: $selectorRef1, label:
Text("Reference 1")) {
ForEach(0 ..< ref1Array.count, id:\.self) {
Text(self.ref1Array[$0])
}
}
Picker(selection: $selectorRef2, label:
Text("Reference 2")) {
ForEach(0 ..< ref2Array.count, id:\.self) {
Text(self.ref2Array[$0])
}
}
Picker(selection: $selectorRef3, label:
Text("Reference 3")) {
ForEach(0 ..< ref3Array.count, id:\.self) {
Text(self.ref3Array[$0])
}
}
})
Section(header: Text("Status"), content: {
Picker(selection: $selectorStockStatus, label:
Text("Condition")) {
ForEach(0 ..< stockStatusArray.count, id:\.self) {
Text(self.stockStatusArray[$0])
}
}
})
Button(action: {
self.selectorZone = 0
self.selectorRef1 = 0
self.selectorRef2 = 0
self.selectorRef3 = 0
self.selectorStockStatus = 0
}, label: {
HStack(){
Spacer()
Image(systemName: "return")
Text("Reset filters")
Spacer()
}
})
}.navigationBarTitle("Filter")
.navigationBarItems(leading: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
}
)
), trailing: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Done")
}
)
))
}.onAppear{
self.ref1Array.insert("***ALL***", at: 0)
self.ref2Array.insert("***ALL***", at: 0)
self.ref3Array.insert("***ALL***", at: 0)
self.stockStatusArray.insert("***ALL***", at: 0)
self.zoneArray.insert("***ALL***", at: 0)
}
}
}
struct GoodsItemFilter_Previews: PreviewProvider {
static var previews: some View {
GoodsItemFilterView(ref1Array: ["MAX100", "MAX101", "MAX102"], ref2Array: ["REF2_100", "REF2_101"], ref3Array: ["REF3_100", "REF3_101"])
}
}
and when I call it I can pass over the values of the arrays as arguments:
GoodsItemFilterView(ref1Array: ["MAX100", "MAX101", "MAX102"], ref2Array: ["REF2_100", "REF2_101"], ref3Array: ["REF3_100", "REF3_101"])
Now I have another view which is basically a copy of this one with a few changed names etc
//
// OrderHeaderFilter.swift
// WMS Toolbox
//
// Created by Max on 2020-01-24.
// Copyright © 2020 Max. All rights reserved.
//
import SwiftUI
//import Combine
struct OrderHeaderFilterView: View {
#Environment(\.presentationMode) var presentationMode
#State var orderTypeArray: [String] = []
#State var carrierArray: [String] = []
#State var fromStatus2 = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.fromStatus")
// #State private var fromStatus2 = "040"
#State private var direction = ""
#State private var fromStatus = ""
#State private var toStatus = ""
#State private var orderType = ""
#State var selectorOrderType = 0
#State var selectorCarrier = 0
#State private var selectorIndex = 1
#State private var fromStatusSelectorIndex = 6
#State private var toStatusSelectorIndex = 2
#State private var directions = ["Inbound","Outbound","Both"]
private var orderStatusFromArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
private var orderStatusToArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
#State var orderStatus = OrderStatus.s05
enum OrderStatus: String, CaseIterable, Identifiable {
case s05 = "005"
case s10 = "010"
case s22 = "022"
case s25 = "025"
case s30 = "030"
case s35 = "035"
case s40 = "040"
case s45 = "045"
case s46 = "046"
case s60 = "060"
var id: String { rawValue }
}
enum Direction: String, CaseIterable{
case outbound = "1"
case inbound = "2"
case both = "3"
init(type: String) {
switch type {
case "1": self = .outbound
case "2": self = .inbound
case "3": self = .both
default: self = .both
}
}
var text: String {
switch self {
case .outbound: return "Outbound"
case .inbound: return "Inbound"
case .both: return "Both"
}
}
}
init(){
//nothing here
}
var body: some View {
return NavigationView{
Form{
HStack{
Text("Direction")
Spacer()
Picker(selection: $direction, label:
Text("Direction")) {
ForEach(directions, id:\.self) {
status in
Text(status)
}
}
.pickerStyle(SegmentedPickerStyle())
}
Picker(selection: $fromStatus, label:
Text("From Status")) {
ForEach(orderStatusFromArray, id:\.self) {
status in
Text(status)
}
}
Picker(selection: $toStatus, label:
Text("To Status")) {
ForEach(orderStatusFromArray, id:\.self) {
status in
Text(status)
}
}
}.navigationBarTitle("Filter")
.navigationBarItems(leading: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
}
)
), trailing: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Done")
}
)
))
}.onAppear{
self.direction = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.direction")!
self.fromStatus = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.fromStatus")!
self.toStatus = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.toStatus")!
self.orderTypeArray.insert("***ALL***", at: 0)
self.carrierArray.insert("***ALL***", at: 0)
}
}
}
struct OrderHeaderFilter_Previews: PreviewProvider {
static var previews: some View {
OrderHeaderFilterView()
}
}
and when I call it, it is not prompting me to pass over the arrays as arguments:
OrderHeaderFilterView()
What is the difference between those 2 views that the one is asking for arguments on initilization and the other one isn't? To be clear, in the end I want to pass over the arguments, so GoodsItemFilterView() is doing exactly what I need.
EnvironmentObjects are not passed via init method they are implicitly injected. The orderTypeArray and carrierArray have already initial values. So OrderHeaderFilterView() does not prompt you for arguments.
Found the issue(s):
I had this piece in the code
init(){
//nothing here
}
This needs to be removed, otherwise it will not ask for any variables.
The other issue is the one I don't understand:
private var orderStatusFromArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
private var orderStatusToArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
If I change the var to let, it works as expected. Another option is to remove the private at the beginning. So it looks like as soon as you have a
private var ...
in your code, all arguments become private. Maybe I am missing something here but that seems like a bug to me.

Resources