Here is some SwiftUI code in a running app I am working on:
Button(action: action) {
Image(systemName: img)
.imageScale(.large)
}
.simultaneousGesture(LongPressGesture().onEnded { _in
playLngPress = true
showingFlag = true
})
My question is about the relationship between the chunk of code executed when making a long-press on the button and the chunk of code executed when making a simple tap. I first noticed that when making a long-press, the action (for a simple tap) was also executed along side.
Though I do not think this is how it should be, I made a work-around to have things go my way.
More recently (after moving to iOS 15, though I am not 100% sure this is the Reason) I noticed that the action for the tap is not always performed when making a long-press.
Can anyone precisely explain how this is working ?
Related
I am having an issue with refundRequestSheet. So basically, my issue is that everything is presented just fine, I can get through the refund process, but at the end of the process where we are having done button, after I press the done button nothing is happening, result is not being called - it looks like the button is not actually calling any action.
The only way for me to close the screen is to actually swipe the sheet down, and then the result of onDismiss sometimes is failure and sometimes is a success.
I also investigated other type of sheets such manageSubscriptionsSheet, and this is having the same issue basically.
I also tried both simulator and the actual device, still nothing.
.refundRequestSheet(for: viewModel.transactionID ?? 0, isPresented: $viewModel.showRefundSheet, onDismiss: { _ in
viewModel.showRefundSheet = false
})
:-) 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!
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.
In the Apple Tutorial for SwiftUI, under Animating Views and Transitions, what shows on the preview at the end of the tutorial (bottom of the page) isn't what happens when I download the project files and run it on Xcode.
Tutorial: https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions
Project Files (you can download from the tutorial page as well): https://docs-assets.developer.apple.com/published/4790d3c55e/AnimatingViewsAndTransitions.zip
The problem:
At the end of the tutorial, in HikeView, when you press "Heart Rate", "Pace" or "Elevation", the graph should change to the respective graph (which is what they show in the preview). However, when I run it on Xcode, the graph just shifts up and down without any other changes.
When I tried to fix the problem on my own I noticed that removing .transition(.slide) from HikeGraph.swift seemed to solve the problem. But why does this transition cause that issue and is there a way to make the graphs work as intended without sacrificing the slide transition?
I am seeking a fix for the graphs, and if possible, a reason as to why the transition was causing the graphs to work incorrectly.
This can be solved without removing the .transition(.side)
Use :
ForEach(data.indices, id: \.self) { index in
GraphCapsule(
index: index,
height: proxy.size.height,
range: data[index][keyPath: self.path],
overallRange: overallRange)
.colorMultiply(self.color)
.transition(.slide)
.animation(.ripple(index: index))
}
instead of:
ForEach(data.indices) { index in
GraphCapsule(
index: index,
height: proxy.size.height,
range: data[index][keyPath: self.path],
overallRange: overallRange)
.colorMultiply(self.color)
.transition(.slide)
.animation(.ripple(index: index))
}
ForEach needs an identifier to properly identify the views created, hence without the identifier, the animation is not working perfectly.
A "tried and true" pattern I've used in my iOS Swift app is that I have several areas with UIButtons. When the button is pressed, it fires off some net code that connects to my API. While this is happening, I have the button text say "please wait, loading" and I disable the button. When the queued action finishes in my callback I have the button text enable and change back to the original state. It works great.
I recently added something not using NSURL/NSDATA (it's just a file writer). I copied all the same GCD queue code and oddly enough it doesn't update the button text.
Here is my code. When you tap the button, the text becomes invisible until the callback finishes, and then it just restores back. Oddly enough if I change my simulator to iPad Pro it actually works (??) and says "please wait, downloading". If I switch to iPhone 6s it doesn't work.
#IBAction func btnGenerateCSV(sender: UIButton) {
//Grab the original text of the button to restore later after done
let originalButtonText = sender.titleForState(UIControlState.Normal)
//Localized is an extension function I wrote.
//As you can see I got crazy here adding all the UI States as a last ditch attempt to see if that was the reason.
sender.setTitle(Localized("Downloading"), forState: UIControlState.Normal)
sender.setTitle(Localized("Downloading"), forState: UIControlState.Disabled)
sender.setTitle(Localized("Downloading"), forState: UIControlState.Highlighted)
//I've tried moving this before the setTitle. No avail.
sender.enabled = false
//I've tried the other queues as well, and even just tried dispatch_async(dispatch_get_main_queue()) but no luck
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { [unowned self] in
CsvReportWriter.GenerateReport()
{
r in
dispatch_async(dispatch_get_main_queue()) {
FileManager.WriteToFile(r, filename: self.filename)
if FileManager.FileExists(self.filename) {
self.docController = UIDocumentInteractionController(URL: FileManager.GetURLOfFile(self.filename))
self.docController.presentOptionsMenuFromRect(sender.frame, inView:self.view, animated:true)
}
//Restore button state and text b/c we're done
sender.enabled = true
sender.setTitle(originalButtonText, forState: UIControlState.Normal)
}
}
}
}
Any ideas? If I swap out the CsvReportWriter.GenerateReport() code with some other async code that does a call out to my API, it works.
Thanks so much!
So I wanted to post an answer that I thought was interesting - I had a different account logged in to my iPad Pro simulator.. and noticed that the label correctly appeared. In iPhone 6 it did not. I was logged into a different account.
So what was the difference? There were about 1,000,000 test rows to generate in the iPad pro, and 3 in the iPhone, so everything finished so fast there was no UI to even update.
I introduced a delay just as a test, and noticed that in fact it works / exports so that's actually what it was.
So it turns out things were running so fast there wasn't even time to update to 'please wait' lol
Sorry to bother anyone but feel free to use the above code as it should be effective to do what you need :)