strange behaviours viewDidAppear not called - ios

I call a function to open a subview from my parent view.
I see that viewDidLoad is called. In my viewDidLoad is no code.
The strange behaviour is, that sometimes viewDidAppear: is not called even I have not change any code.
Of course there can be a lot of reasons, but I do not know how to debug. I am looking for a way to find out where the process hangs at the time when viewDidLoad is finished.
Does anyone have a hint for me or does anyone know what could be the problem? I mean sometimes it works sometime not. Is viewDidAppear: depending on the parentsview or is there something wrong in the subview?
func showSubview() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("Subview") as! SubViewController
self.presentViewController(vc, animated: true, completion: nil)
//self.showViewController(vc, sender: self);
}
UPDATE
I am calling the showSubview from a async task , I guess this is not good. What do you think and how shall I do it better ? Is there any complition handler with dispatch_async ?
dispatch_async(backgroundQueue, {
dosomething_long();
showSubview();
})
UPDATE 2
The problem is, that I am opening the subview from a background task. I should not do this. But the question is how can I call the subview when the background task is finished. Is there any completion handler for dispatch_async call ?
SOLVED WITH HELP OF GANDALF
I am stupid :-) I should call the subview in the main thread:
dispatch_async(backgroundQueue, {
dosomething_long();
dispatch_async(dispatch_get_main_queue(), {
showsubview();
});
})

The very obvious reason for why program counter may not go inside methods is that app could be running in the release mode.
Now as we can see after updated question that this is not the reason and there is a UI operation happening at background queue.
As per iOS development guidelines all UI operation should happen on main thread, you should try executing that on main thread instead of a background thread.
Try the below code:
dispatch_async(backgroundQueue, {
dosomething_long();
dispatch_async(dispatch_get_main_queue(), {
showsubview();
});
})

The question is; is the view created before? Is it caused when you reopen your app from background?
You can check the lifecycle from here and I think your problem is occurred because of that.
iOS 7 - Difference between viewDidLoad and viewDidAppear

Related

Display a progressHud before the network call in swift

In my code I am executing a network call which takes few seconds to complete. While it is executing I want to display a progresshud in the background. But the problem is the progresshud does not appears before the network call. It appears right after the network call finishes. I can not understand the issue.
My code is below.
func draw() {
if !self.drawing) {
self.progressHud.show(in: self.view)
self.drawing = true
self.drawImage() // this is the function that takes time to execute
}
else if (self.isTransformViewEnabled){
self.drawing = false
}
}
Please help
I think you are calling the network call synchronously. So it waits the request to complete for showing hud. You can call in on Main thread on async
DispatchQueue.main.async {
//your code here
}
This is happening because you are making the network call on the main queue synchronously. Hence the UI is updated after the call is completed.
Please make the network call asynchronously in a different queue and the HUD will show up on the screen.
DispatchQueue.main.async {
self.drawImage()
}
Let me know if it works for you.
Happy to help.
Thanks.

SKNode's action run completion block does not get called

I have a watchOS 4 app which displays SpriteKit animations (SKActions) on top of the UI. Everything works fine in simulator and also on device first couple of times, then after some time when app is in background, and it is started, animations just freeze and completion block for the most long-lasting animation is not called. Any idea what might be the issue?
This is how I run my actions, caller is waiting for completion closure in order to hide the spritekit scene:
private func runActions(with icon: SKShapeNode?, completion: #escaping () -> Void) {
if let icon = icon, let scaleAction = scaleAction, let bg = background {
self.label?.run(fadeInOutAction)
icon.run(scaleAction)
icon.run(fadeInOutAction)
bg.run(backgroundAction, completion: completion)
} else {
completion()
}
}
And yes, I am aware that SKScene is paused when app moves to background. I am doing this in willActivate of my InterfaceController:
if scene.scene?.isPaused == true {
scene.scene?.isPaused = false
}
I want to emphasize that this works first always. It begins to fail after the app has been backgrounded for some time. Especially if I start the app from complication and try to immediately fire these animations, then this freezing happens.
Can I answer my own question? I guess I can? Here goes:
I finally solved this. It turns out that the WKInterfaceScene in WatchKit has ALSO an isPaused property that you need to turn false sometimes. So now in willActivate of my InterfaceController I will also check that and turn it false if it is true. Since I made this change, I haven't seen a single hiccup, freeze or anything weird anymore.
Case closed, I guess. I leave this here for future generations who might face this issue.

Completion block in (animateWithDuration:animations:completion:) is unpredictably delayed

The code is too huge to post it here. My problem is the following. When I call animateWithDuration:animations:completion: (maybe with options) with duration == 0.3 it doesn't mean that the completion block will be called through the same delay. It is called through 2 seconds instead and it is too long for me.
This big delay usually appears before memory warnings but sometimes may work as expected.
Could anybody explain what may cause such a strange behaviour?
Are there any timers involved, like is the animation timer-triggered?
I had a similar problem when my animation was timer-triggered. It turned out the animation was started more than once. animationOngoing flag stopped animation from being started again before finishing.
// Timer function
func timerTextToggle(timer: NSTimer) {
if self.animationOngoing == false {
self.flipAnimation()
}
}
// Animation function
func flipAnimation() {
// important note: it's UIViewAnimationOptions,
// not UIViewAnimationTransition
self.animationOngoing = true
if self.animationToggle == false {
UIView.transitionFromView(self.singleTapLabel!,
toView: self.doubleTapLabel!,
duration: animDuration,
options: UIViewAnimationOptions.TransitionFlipFromBottom,
completion: {
(value: Bool) in
self.animationOngoing = false
})
} else {
UIView.transitionFromView(self.doubleTapLabel!,
toView: self.singleTapLabel!,
duration: animDuration,
options: UIViewAnimationOptions.TransitionFlipFromTop,
completion: {
(value: Bool) in
self.animationOngoing = false
})
}
self.animationToggle = !self.animationToggle
}
I experienced a similar problem to this, although without further information on your scenario, I don't know if this also applies to your issue.
I was calling becomeFirstResponder on a UITextField in the completion block of my animateWithDuration:delay:options:animations:completion. Logging showed the completion block was being called in a timely manner, but the keyboard was taking several seconds to show. This was only occurring on first launch of the keyboard.
This answer helped me solve this... turned out this was somehow linked to the iOS Simulator. This issue did not occur when I wasn't debugging the app... another classic example of chasing a bug for hours in the simulator that didn't actually exist.
The cause of this problem is found out. It is a lot of UIWebView objects rendered in the main thread. And it seems impossible to prevent their loading. Anyways time profiler show that a lot of time is spent to render them even if they are not visible on the screen.
And yes, I can't release them before memory warning event because of requirements

Why is popViewControllerAnimated taking so long to run?

I have a secondary viewController that allows me to delete images from the camera roll. The problem is, the completionHandler fires like it's suppose to, but the popViewController doesn't actually seem to run for about 8 seconds. It definitely fires, because I can see the optional output. And I checked just doing the pop, and it runs correctly. I checked the viewWillDisapear event, and it fires late as well, which I expected considering the nav controller hadn't popped the view current viewController yet.
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(assetsToDelete)
return
}, completionHandler: { success, error in
if success {
println("success")
println(navigationController.popViewControllerAnimated(true))
println("so slow")
}
if let error = error {
println(error)
}
return
})
This is what the documentation says:
Photos executes both the change block and the completion handler block
on an arbitrary serial queue. To update your app’s UI as a result of a
change, dispatch that work to the main queue.
The navigation controller needs to be executed from the main thread, so you need to wrap the call to something like
dispatch_async(dispatch_get_main_queue()) {
navigationController.popViewControllerAnimated(true)
}
For Swift 3
DispatchQueue.main.async() {
self.navigationController?.popViewController(animated: true)
}

My iOS app freezes but no error appears

Does any body know what I need to check if app freezes after some time? I mean, I can see the app in the iPhone screen but no view responds.
I did some google and i found that, i've blocked the main thread somehow.
But my question is how to identify which method causes blocking of main thread? is there any way to identify?
Launch your app and wait for it to freeze. Then press the "pause" button in Xcode. The left pane should show you what method is currently running.
Generally, it is highly recommended to perform on the main thread all animations method and interface manipulation, and to put in background tasks like download data from your server, etc...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to perform in background
dispatch_async(dispatch_get_main_queue(), ^{
//call back to main queue to update user interface
});
});
Source : http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks
Set a break point from where the freeze occurs and find which line cause that.
Chances may be,Loading of large data,disable the controls,overload in main thread,Just find out where that occurs using breakpoints and rectify based on that.
I believe it should be possible to periodically check to see if the main thread is blocked or frozen. You could create an object to do this like so:
final class FreezeObserver {
private let frequencySeconds: Double = 10
private let acceptableFreezeLength: Double = 0.5
func start() {
DispatchQueue.global(qos: .background).async {
let timer = Timer(timeInterval: self.frequencySeconds, repeats: true) { _ in
var isFrozen = true
DispatchQueue.main.async {
isFrozen = false
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + self.acceptableFreezeLength) {
guard isFrozen else { return }
print("your app is frozen, so crash or whatever")
}
}
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .default)
runLoop.run()
}
}
}
Update October 2021:
Sentry now offers freeze observation, if you don't wanna roll this yourself.
I reached an error similar to this, but it was for different reasons. I had a button that performed a segue to another ViewController that contained a TableView, but it looked like the application froze whenever the segue was performed.
My issue was that I was infinitely calling reloadData() due to a couple of didSet observers in one of my variables. Once I relocated this call elsewhere, the issue was fixed.
Most Of the Time this happened to me when a design change is being called for INFINITE time. Which function can do that? well it is this one:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
Solution is to add condition where the function inside of viewDidLayoutSubviews get calls only 1 time.
It could be that another view is not properly dismissed and it's blocking user interaction! Check the UI Debugger, and look at the top layer, to see if there is any strange thing there.

Resources