SwiftUI Unexpectedly NavigationLink pops automatically - ios

I have a simple use case where a screen pushes another screen using the NavigationLink. There is a strange behaviour iOS 14.5 where the pushed screen is popped just after being pushed.
Code:
NavigationLink(destination: EmptyView()) { EmptyView()}
I manage to create a sample app where I reproduce it. I believe the cause is the presence of #Environment(\.presentationMode) that seem to re-create the view and it causes the pushed view to be popped.
The exact same code works fine in Xcode 12 / iOS 14.4

I was stuck in this since last week. To fix, I simply added this to my view containing my existing NavigationLinks:
NavigationLink(destination: EmptyView()) {
EmptyView()
}

Related

How to print to the Debug Area with Swift UI in Xcode 13?

I want my print message to be shown in the Debug Area in Xcode 13. There are several questions like this on StackOverflow, some of them suggest using the Debug Mode, which seems to have been removed in Xcode 13.
Here is my ContentView:
struct ContentView: View {
var body: some View {
Button("button", action: {
print("abc")
})
}
}
Though I use print("abc"), when I click the button, there's nothing in the Debug Area. How to show it?
The problem is that you're not running the app. You're just looking at the preview. Press Command-R and run the app in a simulator; tap the button in the simulator.
Note that when you're actually running the app, the debug bar (at the top of the debug area) looks quite different:

Disable autocorrect on an iOS 15 SwiftUI searchable search bar

I have a SwiftUI and Core Data app and have implemented the new iOS 15 search bar API.
.searchable(text: $searchText) // This is a modifier under my List view
However, the search bar has autocorrect, which unexpectedly changes the search when the view disappears or the user commits (it even happens if navigating to a detail view and back). Overall it is a poor user experience.
I cannot find anything in the Apple documentation for disabling autocorrect on this search bar (although it is easily done for a standard TextField with the .disableAutocorrect(true) modifier).
I used a Swift Package for iOS14 that provided a search bar (via UIViewRepresentable), but I would rather use first party APIs if possible, so my question relates specifically to the iOS 15 SwiftUI .searchable API.
The auto correction of the search bar get disabled if you set disableAutocorrection(true) after .searchable(text: $searchText)
List {
//
}
.searchable(text: $searchText)
.disableAutocorrection(true)
disableAutocorrect will be deprecated in a future iOS update (current version: iOS 16.3). Use autocorrectionDisabled instead:
List {
//
}
.searchable(text: $searchText)
.autocorrectionDisabled(true)

SwiftUI TabView not responding

:-) Here comes a probably silly problem with a bit of a preamble sorry!
Long story short - I've been developing an app pulling my hair out due to a bug with the TabView in SwiftUI. I was overlaying the tabs with a ZLayer to add a funky big button in the middle - so when I was running into problems I thought this would be the issue... but this becomes irrelevant in a moment.
For simplicity I started a New project in XCode v11.6, build target iOS 13.6, Selected Tabbed App, created a very simple boilerplate SwiftUI content view with 2 tabs. (For clarity - I did not modify the default code at all). Runs perfectly on the simulator. Plug in my iPhone XS running iOS 13.6.1 (when I first encountered this problem I was on 13.5 so have updated iOS builds whilst this problem has occurred). Run the simple, non modified app on my phone. Loads up "First View", I tap the "Second" tab, nothing. No response at all. This is the same problem I am having in my more complicated app. Occasionally if I keep rebuilding. It will let me switch to "Second View" but then cannot switch back. The Tab Controller just seems to become non-responsive.
Now back to my more complicated app, on the first view there is a scroll view and there is a button with an attached actionSheet. If I pop open the action sheet, then dismiss it (regardless of just calling the .cancel() button or selecting an action), the Tab Controller works perfectly. There is another option which opens another sheet, again as soon as something has been overlayed, everything works as expected until the next app launch.
Tried resetting my MacBook, tried resetting my iPhone, tried uninstalling the build then rebuilding, cleaning the build folder, tried creating multiple projects with build targets iOS 13, 13.2, 13.5, 13.6. Problem seems to persist, but every time the simulator works perfectly.
So my questions here are: From my searching I can't find anyone with this problem. Is it just me really? Can anyone with XCode 11.6 and an iPhone XS please simply hit "New Project" -> "Tabbed App" and let me know if this works? (I'm almost wondering if there's a bug in XCode 11.6 when you create an app and everyone is either working on projects that were already created pre XCode 11.6 or already using the beta XCode... But really I am just out of ideas)
Also is there any deeper debugging one can do? Something like actually notifying me for every touch it receives and whether the App sandbox is getting the touch or the OS is (for some reason) keeping the tap gesture? I've never experienced something like this so I can't really see where to start debugging as I don't even know if the app is receiving the tap.
Or frankly - does anyone have any other ideas?
Just for clarity - ContentView looks like this: (Again it's straight from what XCode creates for a Tabbed App)
import SwiftUI
struct ContentView: View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection){
Text("First View")
.font(.title)
.tabItem {
VStack {
Image("first")
Text("First")
}
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
VStack {
Image("second")
Text("Second")
}
}
.tag(1)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Thanks in advance and sorry for the long ramble - from a very sleepy dev at wits' end!

Swift: Has anyone had code work properly on their device, but not in simulator?

I was building an application at the beginning of this year up until around March, and then I took a break and recently wanted to pick it up again. However, my code was behaving differently from when I last used it. I had not made any changes to the code, and strangely even past versions from months prior also weren't working, and those certainly weren't changed.
From some troubleshooting here are things that worked based on this code:
Here is the main ContentView class
var body: some View {
return ZStack {
setColor
.edgesIgnoringSafeArea(.all)
.statusBar(hidden: true)
VStack(alignment:.trailing){
VStack{
DisplayBar(majorKeyManager:majorKeyManager)
}.zIndex(2.0)
VStack{
GuitarView(guitarModel:guitarModel)//.position(x:556, y:220)
}.padding(.bottom, 20)
VStack{
PianoView(pianoModel: pianoModel)//majorKeyManager: majorKeyManager, audioEngine: audioEngine)//.position(x:556,y:600)
}
}
}.edgesIgnoringSafeArea(.top)//.padding(10)
}
Then here is the GuitarView
struct GuitarView: View {
#ObservedObject var guitarModel:GuitarModel
var body: some View {
var needsMarker = false
var needsIndexMarker = false
var needsCapo = false
return HStack (spacing:0){
VStack{
ForEach(0..<guitarModel.numStrings){ stringIndex in
VStack{
Here is the PianoView
struct PianoView: View {
#ObservedObject var pianoModel:PianoModel
var body: some View {
return VStack(alignment:.leading){
ZStack{
HStack (spacing:2){ // DRAW WHITE KEYS
ForEach(0..<self.pianoModel.whiteKeyMap.count){ (keyIndex) -> KeyButtonView in
I didn't think it was important to show the details of what is happening within GuitarView/PianoView. They are structured very similarly, except the difference is the PianoView always behaves properly, while the GuitarView is hit or miss. I placed a red line in the GuitarView image that shows where it is not entering the ForEach, yet it is meeting the conditions of the ForEach (the variable it is checking is always 12).
Both views are being updated based on their respective models which are ObservedObjects. The model is correctly being updated for both the Guitar and Piano. However, GuitarView gets called once when the initial program is launched, but is sometimes never called again. However, this varies depending on whether I'm running it within device simulator, versus a real device and which XCode version I am using.
On XCode 11.4.1, the code does not work properly on the device simulator on any device (Mac, iPhone, iPad, etc.). However, it does run properly on real devices like an iPhone and an iPad. I tried reinstalling XCode but that changed nothing.
Then, I tried rolling back my version of XCode to 11.2.1, and the code runs properly in the iPad simulator, but not as a Mac simulator.
Has anyone had any similar problems to this? I can't seem to figure out why this code gets called once, but then never gets called again despite its ObservedObject being clearly updated, and another nearly identical view (PianoView), is clearly working.

AttributeGraph: cycle detected through attribute using #State in playgrounds

I'm trying to use SwiftUI's #State in playground. Here's my code:
import UIKit
import PlaygroundSupport
import SwiftUI
struct ContentView: View {
#State private var showGreeting = false
var body: some View {
Form {
Button(action: {
self.showGreeting.toggle()
}) {
Text("Toggle Greeting")
}
if showGreeting {
Text("Hello World!")
}
}
}
}
let viewController = UIHostingController(rootView: ContentView())
PlaygroundPage.current.liveView = viewController
I can see the button on the live view. But if I click it, it doesn't show the text. A second click does reveal the text. Further clicks don't seem to make any difference visually.
On each click, I get the following message in log area:
=== AttributeGraph: cycle detected through attribute 38 ===
Need some help understanding what this means in this context and why the state is not behaving naturally.
Note: I'm running macOS Mojave, so I don't have option of SwiftUI previews. I'm making do with live preview of playgrounds.
There's nothing wrong with your SwiftUI, and it runs fine when pasted into a new project.
If you change the Form to a VStack, it works fine in the playground. Using a List gives you the same error. Maybe a bug, I'd report it to Apple.
In my experience, if you're stuck on Mojave for the time being, just set up a project where you can run the simulator. It takes just a couple seconds to compile and run, and you wind up saving time when your SwiftUI playgrounds crash, get weird errors like this, etc.

Resources