swiftUI rock, paper, scissors game - swiftui

I'm make this game to learn about Swift, but am having some issues with it. This error appears:
(LocalizedStringKey) -> Text is not convertible to
(LocalizedStringKey, String?, Bundle?, StaticString?) -> Text
VStack(spacing: 30){
Text("Continue")
.foregroundColor(.white)
.font(.headline)
.fontWeight(.black)

This is a horrible kind of error. Of course, nothing is wrong with that TextView: the problem is somewhere else. But Xcode isn't able (yet) to find the real error. So please make sure that you use the newest version of Xcode.
You will have to comment out the code piece by piece, and then find out what line gives the real problem.
If you need help with that, please also post the code that is around the fragment you posted.

Related

SwiftUI, Alert disrupts NavigationLink?

previous threads on this question have non-swift answers that I can't figure out how to implement in SwiftUI. Please note that I'm using Xcode 12.5.
I created a playground in an attempt to post a "demo" version of the code, but wouldn't you know it, the playground runs flawlessly. So, going to post little bits of the code without making your eyes bleed. I'm getting this warning:
pushViewController:animated: called on <TtGC7SwiftUI41StyleContextSplitViewNavigationControllerVS_19SidebarStyleContext 0x103021c00> while an existing transition or presentation is occurring; the navigation stack will not be updated.
Here's how the code operates:
Navigate from View A to View B. In View A's NavigationLink, I run this:
NavigationLink(destination: MainScreen(user: user)) {
CapsuleButtonView(txt: "LOGIN")
}.simultaneousGesture(TapGesture().onEnded {
user.login()
})
.padding([.leading, .bottom, .trailing])
user.login() checks to see if the username and password are entered (bindings). If not, it flips a #Published Bool which the view that runs the above code uses to trigger the alert message:
.alert(isPresented: $user.alert, content: {
Alert(title: Text("Message"), message: Text(user.alertMsg), dismissButton: .destructive(Text("Ok")))
})
The Alert pops as it should, but I think it does it after the NavigationLink has started to navigate away from View A to View B. So, after the Alert pops, the NavigationLink's simultaneousGesture block still runs the code in it (I checked with print statement), but does NOT navigate to View B.
Thanks for your help guys. After 3 days of looking a this, I'm about to tear out what little hair I have left.
Try adding navigationViewStyle(.stack)
NavigationView {
//Your view
}
.navigationViewStyle(.stack)

SwiftUI SecureField: How to achieve the same character obscuring behaviour as in UIKit?

My problem is SecureField in SwiftUI doesn’t display characters input by the user for any time at all, it just directly shows the '•' symbol for each character as it's typed - whereas in UIKit, UITextField (with isSecureTextEntry = true) shows the latest character for a second before hiding it behind '•'.
UX testers at my company have requested I bring back the "old behaviour" - but this behaviour doesn't seem part of any public API.
Interestingly this goes for UITextField custom classes injected into SwiftUI using UIViewRepresentable too - they behave in the "SwiftUI way" described above. So there's some contextual behaviour modification going on in SwiftUI for all secure UITextField behaviour? I'd have to completely rewrite my SwiftUI form into a full UIViewController to get back the behaviour (modally pushed UIViewControllers with secure UITextFields do exhibit the desired behaviour.)
Is this a sort of sideline bug in SwiftUI? I see the same thing for SwiftUI in both iOS13 and 14. Anyone seen a workaround or solution?
-EDIT-
After #Asperi's great explanation below, I noticed that my UITextField custom classes injected into SwiftUI using UIViewRepresentable were forcing this behaviour by unnecessarily setting the text binding in the updateUIView call. Using a Coordinator only to deal with text logic fixed the problem for me when using this method.
The observed effect is due to immediate apply to bound string state and immediate react/rebuild of view.
To bring desired behavior beck we need to postpone somehow state update and thus give a chance for SecuredField/UITextField to update self without synchronisation with state.
Here is a demo of possible direction (it is not ideal, but a way to go). Tested with Xcode 12.1 / iOS 14.1.
struct DemoSecureFieldView: View {
#State private var password = "demo"
var textBinding: Binding<String> {
Binding(get: { password },
set: { value in
// more logic can be added to delay _only_ if new symbol added,
// and force apply if next symbol came fast
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
password = value
}
}
)
}
var body: some View {
VStack {
SecureField("Placeholder", text: textBinding)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}.background(Color.pink)
}
}

Extract Subview not showing up

I'm building my first SwiftUI app. For some reason when I command+click on an HStack the "Extract Subview" and other options don't show up. Any reason why I'm not seeing these options? I'm using Core Data and UITableView.appearance to remove lines in my List as well. Does it have to do with using core data and my use of UITableView? Couldn't find any answers after researching. Appreciate the help.
Not sure if this is an Xcode issue/bug but as of Xcode 11.4 the "Extract Subview" option (and others) show up only if the Canvas is being shown.
So, enable live preview and then cmd-click on the View you want to extract.
I got the same problem. You should do command + right click.
I was having the same problem but could not solve it with the presented solution. Came to realize that you need to have an Object embedded in another to be able to extract it.
In the example below, I would not be able to extract the HStack, but it will show up in the VStack.
struct ContentView: View {
var body: some View {
HStack(){
Image("sal")
.resizable()
.frame(width: 100, height: 100)
.aspectRatio(contentMode: .fit)
.cornerRadius(20)
.padding(.trailing)
VStack(alignment: .trailing){
Text("Camino La Angostura")
Text("2.5 Km.")
}
}
}
}

SwiftUI Lists & OnAppear

I have noticed some odd behavior on OnAppear events for List views. I'd think the OnAppear closure would run whenever a view appears on the screen, but it appears to run all at once when the List is loaded.
For example the following code:
#State var rows: [String] = Array(repeating: "Item", count: 20)
var body: some View {
List(0..<rows.count, id: \.self) { index in
Text(verbatim: self.rows[index])
.onAppear {
print("BOOOOM")
}
.frame(height:400)
}
}
...I would expect the print command to run a couple times on load, then continue to print as I scrolled down. Instead it prints 20 times at once, then again as I start to scroll down.
Any thoughts?
I think it is behaving as expected.
For me it printed 15 times on a Simulator with iPhone 7 and all 20 times on a Simulator with an iPhone 11.
I made a slight change to the print("BOOOOM \(index)")
There is probably a balance with performance and resources that the List abides by in the background.
Load too little and the user will "Get stuck" if scrolling too fast vs loading too much and slowing the scroll animation.
This is has been a thorn in my proverbial claw for a good month now
I can't explain why this is happening, but it's something to do with Lists in iOS 14, and custom cell heights. If you remove .frame(height: 400) you'll notice that "Boom" is printed the correct number of times. When your cell height has been enlarged beyond just a normal Label, iOS will still load the same number of cells, but then backtrack for those off screen (have a look at the .onDisappar() view modifier).
What can you do?
1. Update to iOS 15
This behaviour is correct in iOS 15 and 13, so if you can, just up your minimum deployment target and say goodbye to iOS 14
2. Switch to LazyVStack
True to their name: LazyVStack and LazyVGrid don't do anything more than they have to, and will "Boom" the correct number of times. The downside is that you lose iOS 13 compatibility, and all the automatic layout magic that comes with List
If you have since found a way to make onAppear() work correctly in List for iOS 14, PLEASE let me know

SwiftUI and EmptyViews

I have the following code example:
struct ContentView: View {
#State var showText = true
var body: some View {
VStack {
if self.showText {
Text("Hello")
}
//else {
// EmptyView()
// }
}
}
}
When it runs I get the text showing as normal. However, if I set showText to false it still compiles and runs as normal even though the VStack contains nothing - it just doesn't show anything when run. If I uncomment the else clause the example also runs as expected. If I remove all the contents of the VStack however, the example won't compile as nothing is returned.
So my questions are:
Is SwiftUI silently adding an EmptyView when nothing is in the VStack when showText is false ?
Should I really be adding the else clause and return an EmptyView ?
The question isn't completely academic either as I have a use case where I would prefer the whole view to be more or less 'thrown away' rather than have EmptyViews in the hierarchy, though my understanding of SwiftUI is pretty limited at the moment so it may not matter.
VStack is a function builder so it expects to get some value back from the closure. In the case of the if it invokes the buildEither version of the function builder so that satisfies the condition that its not empty. See the function builder proposal. Any ways you should not worry about the EmptyViews. A SwiftUI.View is just a blue print to build the actual view (as apple calls it a cheap value on the stack). It is not the real view object in memory and on the graphics card, as with a UIView or CALayer. The rendering system is going to translate your EmptyView into a noop. SwiftUI.Views get created and discarded all the time and are designed to be cheap unlike UIViews, and the system diff's the SwiftUI.View tree and only applies the delta to the real views in graphics memory similarl to how Flutter, React, and Android Compose work.