How to prevent UIVisualEffectView crash
Issue #124
We all know that there’s a potential crash with UIVisualEffectView on iOS 11. The fix is to not add sub views directly to UIVisualEffectView, but to its contentView. So we should change
1 | effectView.addSubview(button) |
to
1 | effectView.contentView.addubView(button) |
Here we don’t need to perform iOS version check, because effectView.contentView works for any iOS versions.
Potential cases for crashes
Here are some cases you can potentially cause the crashes
Strange namings
Normally we name our UIVisualEffectView as blurView, effectView. But there’s times we name it differently like navigationView, containerView, boxView, … This way we may completely forget that it’s a UIVisualEffectView 🙀
1 | containerView.addSubview(button) |
Custom loadView
Sometimes it’s convenient to have our UIViewController 's view as a whole blur view, so that all things inside have a nice blur effect background
1 | class OverlayController: UIViewController { |
By setting our blurView as view in loadView, we have no idea afterwards that view is actually a UIVisualEffectView 🙀
Inheritance
What happen if we have another UIViewController that inherits from our OverlayController, all it knows about view is UIView, it does not know that it is a disguising UIVisualEffectView 🙀
1 | class ClocksController: OverlayController { |
Superclass type
Sometimes declare our things but with protocol or superclass types. Consumers of our API have no clue to know that it is UIVisualEffectView 🙀
1 | let view: UIView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) |
Here it appears to us that view is of type UIView
Legacy codebase
Now imagine you ‘ve handled a legacy codebase to deal with. Perform finding and replacing all those things related to UIVisualEffectView is very hard task. Especially since we tend to write less tests for UI
Making it impossible to crash
I like concept like Phantom type to limit interface. Here we’re not using type but a wrapper
1 | final class BlurView: UIView { |
Here we override addSubview to always add views to effectView.contentView. In the init method, we need to call insertSubview instead because of our overriden addSubview
Now BlurView has a blur effect thanks to is underlying UIVisualEffectView, but expose only addSubview because of its UIView interface. This way it is impossible to cause crashes 😎
1 | let blurView = BlurView(style: .dark) |