App crashes on call to setNeedsDisplay for custom button
Asked Answered
V

1

-1

I've custom button, app crashes randomly on call to setNeedsDisplay. How to fix this crash?

class MyVC: UIViewController {

    @IBOutlet weak var customButton: CustomButton!

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        self.customButton.setNeedsDisplay() // Random crash here
    }
}

class CustomButton: UIButton {
    // ....

    override func setNeedsDisplay() {
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: UIScreen.main.bounds.size.width-29, bottom: 4.0, right: 0)
    }
}

Crash log

Crashed: com.apple.main-thread EXC_BREAKPOINT 0x0000000104fb3f48

Crashed: com.apple.main-thread 0 myapp 0x104fb3f48 MyVC.viewWillTransition(to:with:) + 63 (MyVC.swift:63) 1 myapp
0x104fb3f88 @objc MyVC.viewWillTransition(to:with:) () 2 UIKitCore 0x1ee286708 -[UIViewController viewWillTransitionToSize:withTransitionCoordinator:] + 868 3 UIKitCore 0x1ee1e596c -[UINavigationController viewWillTransitionToSize:withTransitionCoordinator:] + 84 4 UIKitCore 0x1ee286708 -[UIViewController viewWillTransitionToSize:withTransitionCoordinator:] + 868 5 UIKitCore 0x1ee1b46dc -[UITabBarController viewWillTransitionToSize:withTransitionCoordinator:] + 48 6 UIKitCore 0x1ee27ba18 +[UIViewController _performWithoutDeferringTransitions:] + 112 7 UIKitCore 0x1ee292a24 -[UIViewController(AdaptiveSizing) _window:viewWillTransitionToSize:withTransitionCoordinator:] + 580 8 UIKitCore 0x1ee8537c4 59-[UIWindow _rotateToBounds:withAnimator:transitionContext:]_block_invoke + 188 9 UIKitCore 0x1eeca08f0 +[UIView(Animation) performWithoutAnimation:] + 104 10 UIKitCore
0x1ee853618 -[UIWindow _rotateToBounds:withAnimator:transitionContext:] + 412 11 UIKitCore 0x1ee855e24 -[UIWindow _rotateWindowToOrientation:updateStatusBar:duration:skipCallbacks:] + 1184 12 UIKitCore 0x1ee8564d8 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 516 13 UIKitCore 0x1ee8558d8 -[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 128 14 UIKitCore 0x1ee854584 __57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 124 15 UIKitCore 0x1ee854488 -[UIWindow _updateToInterfaceOrientation:duration:force:] + 560 16 UIKitCore 0x1ee0dcfe0 -[_UICanvasMetricsCalculator updateMetricsOnWindows:animated:] + 624 17 UIKitCore
0x1ee0e1990 -[_UICanvas _computeMetrics:] + 180 18 UIKitCore
0x1eeca08f0 +[UIView(Animation) performWithoutAnimation:] + 104 19 UIKitCore 0x1ee0e04a8 -[_UICanvas _performActions:withOverrideSettings:] + 172 20 UIKitCore 0x1ee821b0c -[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:] + 404 21 UIKitCore 0x1ee8232f0 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_2 + 124 22 FrontBoardServices 0x1c4b51768 -[FBSSceneSnapshotAction _executeNextRequest] + 256 23 FrontBoardServices 0x1c4b517b8 -[FBSSceneSnapshotAction _executeNextRequest] + 336 24 FrontBoardServices 0x1c4b51358 -[FBSSceneSnapshotAction executeRequestsWithHandler:completionHandler:expirationHandler:] + 276 25 UIKitCore 0x1ee823220 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke + 268 26 UIKitCore 0x1ee822848 -[UIApplication _beginSnapshotSessionForScene:withSnapshotBlock:] + 744 27 UIKitCore 0x1ee822f74 -[UIApplication _performSnapshotsWithAction:forScene:completion:] + 208 28 UIKitCore 0x1ee822e40 -[UIApplication _handleSnapshotAction:forScene:completion:] + 156 29 UIKitCore 0x1ee81e724 __71-[UIApplication _handleSnapshotAction:forScene:deactivationCompletion:]_block_invoke + 332 30 UIKitCore 0x1ee81e540 -[UIApplication _handleSnapshotAction:forScene:deactivationCompletion:] + 340 31 UIKitCore 0x1ee0d9f58 __98-[__UICanvasLifecycleMonitor_Compatability deactivateEventsOnly:withContext:forceExit:completion:]_block_invoke.261 + 820 32 UIKitCore 0x1ee8220ac _runAfterCACommitDeferredBlocks + 296 33 UIKitCore 0x1ee810bfc _cleanUpAfterCAFlushAndRunDeferredBlocks + 352 34 UIKitCore 0x1ee83da6c _afterCACommitHandler + 116 35 CoreFoundation 0x1c212ed08 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
+ 32 36 CoreFoundation 0x1c2129a30 __CFRunLoopDoObservers + 412 37 CoreFoundation 0x1c2129fac __CFRunLoopRun + 1228 38 CoreFoundation 0x1c21297c0 CFRunLoopRunSpecific + 436 39 GraphicsServices
0x1c432a79c GSEventRunModal + 104 40 UIKitCore
0x1ee816c38 UIApplicationMain + 212 41 myapp
0x104db7454 main + 20 (ProfileVC.swift:20) 42 libdyld.dylib
0x1c1bed8e0 start + 4

Vanir answered 1/9, 2019 at 23:15 Comment(4)
Edit your question to include the complete error message from the crash.Turbinal
you posted the code self.custom.setNeedsDisplay() and say that is the line that crashes. However, you don't show the definition of custom. Should that be customButton, not custom? Don't paraphrase your code - post the actual code.Wildeyed
I've updated question, it's customButton.Vanir
You didn't post the error message. The stack trace is irrelevant since you already pointed out the problematic line.Turbinal
W
0

Assuming your line that crashes should actually read:

self.customButton.setNeedsDisplay() // Random crash here

The problem is likely that customButton is nil. You've declared customButton as an implicitly unwrapped optional. That means that the compiler unwraps it for you when you try to reference it. Try adding:

print("self.customButton = \(self.customButton?)")
self.customButton.setNeedsDisplay() // Random crash here

And look at the debug log. I'm guessing that you'll see something like:

self.customButton = Optional(nil)

Wildeyed answered 1/9, 2019 at 23:31 Comment(3)
Yes that is what I also suspected that customButton is nil. Does it mean this button is not initialised in storyboard when this function is called? Do I need to guard this statement to fix this issue?Vanir
When a view controller is instantiated from a storyboard/xib its views aren't loaded immediately. I haven't used viewWillTransition(to:with:) in quite a while, and don't remember if it gets called before the target view controller's views are loaded or not. This would be a good case for optional chaining: self.customButton?.setNeedsDisplay() (Note the added question mark.) In optional chaining, if the optional being unwrapped is nil, the method doesn't get called, but execution continues with the next line.Wildeyed
Thank you very much for detailed explanation. I'll fix it in the way you have explained.Vanir

© 2022 - 2024 — McMap. All rights reserved.