Nice find, though I'm not entirely sure what effect it has, especially as the new 'data' property isn't explicitly called. How did you find it? But like you say, with the lack of documentation, one just has to explore and experiment. From my brief look at functions associated with DynamicViewContent, it may have been a cleaner way to implement my horizontal swipable view, but I digress.Unfortunately, when two other components are reintroduced (only when they are both there, not either or), SIGABRT's still occur. However, unless we find a way to ensure a 'state' var updates, this is getting further and further away from the original thread topic (which you explained was simply due to SwiftUI's states strategy) so I should probably leave it here and pursue other options.
Post
Replies
Boosts
Views
Activity
wow, I spent hours testing things but aparently removing the state wrapper for positions wasn't one of them... ooft.I'm just baffled that a completely separate instance was able to (and more importantly only) access the other's values. The blindness and fairly empty documentation of SwiftUI have been a recurring theme for struggles in this project. Depsite the many great concepts and features it has, hindsight has definitely shown I should have chosen a more mature approach/framework for my first foray into apps. Thanks for all your help OOPer.Unfortunately, this fix undoes the fix from the earlier post, and thus in the larger scheme of the app, becomes unusable as navigating to a Dots(_:) instance causes the SIGABRT crash error.I'll check this post in the morning but I'm thinking I'll just have to change the approach and rather than having a button changing an index (which signifies what the current item is), it might have to be a NavigationLink or something.
An example of prints made during quick run in Xcode preview. As you can see, the underlined components are repeated throughout and are the values that actually get used in the view being shown. None of the other positions ever appear.To be clear, after Next has been tapped, the new Dots(_:) instance should only be using the position values seen under "new positions" before the empty newline.// Hit Debug previewnew positionsDotPosition(r: 1, c: 0, jitterScaleX: -0.9684827153965649, jitterScaleY: 0.33412677927887224)DotPosition(r: 3, c: 1, jitterScaleX: -0.405571513766956, jitterScaleY: -0.5655123592406626)DotPosition(r: 1, c: 0, jitterScaleX: -0.9684827153965649, jitterScaleY: 0.33412677927887224)DotPosition(r: 3, c: 1, jitterScaleX: -0.405571513766956, jitterScaleY: -0.5655123592406626)DotPosition(r: 3, c: 1, jitterScaleX: -0.405571513766956, jitterScaleY: -0.5655123592406626)DotPosition(r: 1, c: 0, jitterScaleX: -0.9684827153965649, jitterScaleY: 0.33412677927887224)new positionsDotPosition(r: 0, c: 2, jitterScaleX: -0.26761998822086364, jitterScaleY: -0.21409538770110936)DotPosition(r: 3, c: 2, jitterScaleX: 0.2007303590288385, jitterScaleY: -0.48008864864731593)DotPosition(r: 0, c: 2, jitterScaleX: -0.26761998822086364, jitterScaleY: -0.21409538770110936)DotPosition(r: 3, c: 2, jitterScaleX: 0.2007303590288385, jitterScaleY: -0.48008864864731593)DotPosition(r: 3, c: 2, jitterScaleX: 0.2007303590288385, jitterScaleY: -0.48008864864731593)DotPosition(r: 0, c: 2, jitterScaleX: -0.26761998822086364, jitterScaleY: -0.21409538770110936)// Finish initial load, now interacting...######### NEXT #########new positionsDotPosition(r: 2, c: 2, jitterScaleX: 0.8712347388399257, jitterScaleY: 0.9516460599809775)DotPosition(r: 2, c: 3, jitterScaleX: 0.2477516584036734, jitterScaleY: 0.45822677865038264)DotPosition(r: 1, c: 3, jitterScaleX: 0.30958523909508173, jitterScaleY: -0.7122354433992328)DotPosition(r: 2, c: 2, jitterScaleX: 0.8712347388399257, jitterScaleY: 0.9516460599809775)DotPosition(r: 2, c: 3, jitterScaleX: 0.2477516584036734, jitterScaleY: 0.45822677865038264)DotPosition(r: 1, c: 3, jitterScaleX: 0.30958523909508173, jitterScaleY: -0.7122354433992328)DotPosition(r: 3, c: 1, jitterScaleX: -0.405571513766956, jitterScaleY: -0.5655123592406626)DotPosition(r: 1, c: 0, jitterScaleX: -0.9684827153965649, jitterScaleY: 0.33412677927887224)######### NEXT #########new positionsDotPosition(r: 4, c: 2, jitterScaleX: -0.29471233845995637, jitterScaleY: 0.9377753107486326)DotPosition(r: 1, c: 2, jitterScaleX: 0.14988943796420284, jitterScaleY: -0.8542303116630483)DotPosition(r: 2, c: 1, jitterScaleX: -0.07679392843163457, jitterScaleY: 0.4895482036137371)DotPosition(r: 3, c: 1, jitterScaleX: -0.7264365110057209, jitterScaleY: -0.7451452602829394)DotPosition(r: 4, c: 2, jitterScaleX: -0.29471233845995637, jitterScaleY: 0.9377753107486326)DotPosition(r: 1, c: 2, jitterScaleX: 0.14988943796420284, jitterScaleY: -0.8542303116630483)DotPosition(r: 2, c: 1, jitterScaleX: -0.07679392843163457, jitterScaleY: 0.4895482036137371)DotPosition(r: 3, c: 1, jitterScaleX: -0.7264365110057209, jitterScaleY: -0.7451452602829394)DotPosition(r: 3, c: 1, jitterScaleX: -0.405571513766956, jitterScaleY: -0.5655123592406626)DotPosition(r: 1, c: 0, jitterScaleX: -0.9684827153965649, jitterScaleY: 0.33412677927887224)######### BACK #########new positionsDotPosition(r: 2, c: 3, jitterScaleX: 0.8320728275072029, jitterScaleY: -0.8453647178718258)DotPosition(r: 1, c: 3, jitterScaleX: 0.5753115798672581, jitterScaleY: -0.8042377280170807)DotPosition(r: 1, c: 2, jitterScaleX: 0.28010870718446235, jitterScaleY: 0.11277668898845605)DotPosition(r: 2, c: 3, jitterScaleX: 0.8320728275072029, jitterScaleY: -0.8453647178718258)DotPosition(r: 1, c: 3, jitterScaleX: 0.5753115798672581, jitterScaleY: -0.8042377280170807)DotPosition(r: 1, c: 2, jitterScaleX: 0.28010870718446235, jitterScaleY: 0.11277668898845605)DotPosition(r: 3, c: 1, jitterScaleX: -0.405571513766956, jitterScaleY: -0.5655123592406626)DotPosition(r: 1, c: 0, jitterScaleX: -0.9684827153965649, jitterScaleY: 0.33412677927887224)
TextField can take a number of native Formatter objects, such as NumberFormatter()@State private var myInt = 0
...
TextField("", value: self.$myInt, formatter: NumberFormatter())Any entered values that are deemed invalid by the formatter print as such to the terminal and are ignored.(I'm new here so if any more experienced people think my answer needs a slight correction please let me know, just trying to help)
This is probably what you meant by "do it manually", but I've personally just been using screen snippets (Cmd+Shift+4). Slightly better than Screenshot -> Crop at least.
Interestingly that post didn't come up with me googling efforts, maybe since it doesn't have any activity on it? Nevertheless, thanks for the reply.
Ah yes, an issue I am well acquainted with. I'd recommend updating your original post (which I've marked as the soln) in case others come here.Personally, I went in a slightly different design direction using a rounded rectangle outline, but thank you all the same.
An interesting solution.On the linefunc modifier(_ theModifier: TM) -> some View { is TM supposed to be TextModifier?If so, that causes the error "Protocol 'TextModifier' can only be used as a generic constraint because it has Self or associated type requirements"
Amazing, thank you!Some great ideas in code reorganisation here as well, namely separating the random aspects of a dot's position from the bit requiring the GeomertryReader values, which neatly eliminates the forced conformance of CGSize to Hashable. My initial approach had me thinking quite linearly.May I ask what difference betweenself._positions = State(initialValue: createPositions())and justself.positions = createPositions()is?Thanks again.
Indeed, user defaults was never the problem?That line triggers the associated NavigationLink, ie telling the NavigationLink instance that has the "nav" variable as the value for its "selection" property/parameter. You don't need to fixate on it, as I showed in the last post, the crash is unaffected if you remove it and use a basic NavigationLink that have something instead of the EmptyView().When you said earlier that replacing EmptyView() with Dots() works - this is not really related. What is important is that Dots() is the value of the NavigationLink's "destination", and when trying to navigate to that destination, with a dot quantity value other than 0, it crashes. Furthermore, simply including the same Dots() view on the page (like when you replaced EmptyView() with it) does not trigger the crash. Thus it is strictly related to navigating to the Dots view.Does that help clarify the issue at all?
It activates the associated NavigationLink.It's like havingNavigationLink(destination: Dots(lowerBound + pickerIndex)) {
Text("Continue").modifier(CapsuleButton())
}but allows some extra code to be executed in between the user tapping the link, and the actually navigating to the destination i.e. Dots(num). In my case, it updates the stored UserDefaults. I believe the inspiration came from the solution for https://stackoverflow.com/questions/57130866/how-to-show-navigationlink-as-a-button-in-swiftuiThe issue still occurs if you have the above instead ofNavigationLink(destination: Dots(lowerBound + pickerIndex), tag: 1, selection: $nav) { EmptyView()
Text("Continue")
.modifier(CapsuleButton()) // custom look
.onTapGesture {
// Update storage
let currentHighest = UserDefaults.standard.integer(forKey: "highestDotCovered")
let highest = max(lowerBound + self.pickerIndex, currentHighest)
UserDefaults.standard.set(highest, forKey: "highestDotCovered")
// Go to destination
self.nav = 1
}
With the code as posted, testing in Preview. Live preview, continue goes to a blank screen, in Debug preview, the described error occurs.I just ran a simulator and I get the behaviour of Continue now behaving as you described as well.
Run Debug Preview on View A. When clicking "Continue", it should show View B. Instead, the terminal shows precondition failure: attribute failed to set an initial value: 578 (the number varies). Then in the editor, it auto navigates to the boiler plate AppDelegate to show the error:class AppDelegate: UIResponder, UIApplicationDelegate { // <- Thread 1: signal SIGABRTFor this purpose View A can be ContentView, so I'll quickly edit the post to allow you to run in a tmp xCode project.
Edit: It appears my issue was just a matter of placing the $ binding in the right placeForEach(0..<ops.count) { i in
Toggle("Toggle", isOn: self.$ops[i].isSelected)
.toggleStyle(OperatorToggle(op: self.ops[i]))
}I have a similar setup with a few differences.using ForEach instead of Listinstead of an array of a struct, I have an array of structs conforming to a common protocolthe ForEach is iterating over a 0..<myArray.countSo far I've just tried to use your code but with my variable but my efforts so far have failed.struct OpSelect: View {
@State private var ops: [Operator] = [Add(), Subtract(), Multiply(), Divide()]
var body: some View {
List($ops, id: \.self) { (op : Binding) in
//^ Cannot convert value of type '(Binding) -> _' to expected argument type '(_) -> _'
Toggle(isOn: op.isSelected) {
//^Cannot convert value of type 'Toggle' to closure result type '_'
Text("My Toggle")
}
}
}The code detailing the Operatorsprotocol Operator {
var name: String { get }
var symbol: String { get }
var isSelected: Bool { get set }
// var inverse: Operator { get }
func operate(on a: Int, and b: Int) -> Int
// func inverse(on a: Int, and b: Int) -> Int
}
extension Operator where Self: Hashable {} // https://stackoverflow.com/questions/50966560/make-a-protocol-conform-to-another-protocol
struct Add: Operator {
let name = "add"
let symbol = "plus"
var isSelected = false
// var inverse: Operator = Subtract()
func operate(on a: Int, and b: Int) -> Int {
return a + b
}
}
struct Subtract: Operator {
let name = "subtract"
let symbol = "minus"
var isSelected = false
// var inverse: Operator = Add()
func operate(on a: Int, and b: Int) -> Int {
return a - b
}
}
struct Multiply: Operator {
let name = "multiply"
let symbol = "multiply"
var isSelected = false
// var inverse: Operator = Divide()
func operate(on a: Int, and b: Int) -> Int {
return a * b
}
}
struct Divide: Operator {
let name = "divide"
let symbol = "divide"
var isSelected = false
// var inverse: Operator = Multiply()
func operate(on a: Int, and b: Int) -> Int {
return a / b
}
}
It seems very similar or at least related to this issue https://forums.developer.apple.com/thread/129171#406496I wrapped the top level in a NavigationView as per the other thread as well as removing the functions declared on line 35 and 39, consequently replacing 54 (and 55) withlet availableWidth = max(maxWidth - (CGFloat(cols) * diam), 0)
let colSeparation: CGFloat = availableWidth / CGFloat((cols+1))
let availableHeight = max(maxHeight - (CGFloat(rows) * diam), 0)
let rowSeparation: CGFloat = availableHeight / CGFloat((rows+1))Finally, to deal with the automatic implications of a NavigationView, I added the modifiers.navigationBarTitle("")
.navigationBarHidden(true)to the VStack (ie first view under Nav)and if on iPad you'll likely want.navigationViewStyle(StackNavigationViewStyle())applied to the actual NavigationView.Since the "fix" required an additional and otherwise irrelevant NavigationView, I'm led to believe this is an actual bug in the SwiftUI framework, but can't be certain.