Mobile development: Adding a global button in an iOS app with AppDelegate and SceneDelegate
In this tutorial I’d like to show you how to present UIButton
over all visible views in your mobile app. This solution is designed for iOS 13 and newer versions, so it can be implemented in AppDelegate
or SceneDelegate
.
The solution I am about to present in this tutorial, it should be used only if really needed. In many cases, showing a button via a current View Controller is a better option.
Accessing an app's functionality with a global button
I needed to prepare a framework that would provide access to a functionality from every place in my mobile app.
This could be acheived in many ways. For example with a device shake, specific gesture or, simply, via a global button.
I had wanted my framework to be as simple as possible, so I decided that it will be added just one time, in AppDelegate
or SceneDelegate
.
This effectively forces my 'access button' to be displayed in all available views.
Adding a UIButton to an app's views
So first, I needed to get what view is on top of the app stack. This can be achievied in iOS 13 thanks to this method:
UIApplication.shared.windows
The just-created button can be added to all views just like this:
let button = UIButton()
for window in UIApplication.shared.windows {
window.addSubview(button)
}
Keeping the UIButton's top position
There is yet another important thing. If you want to keep your button on top, you need to change its position:
button.layer.zPosition = +1
If you don’t do this, the button will hide under the presented views and will be neither visible, nor accessible.
This is not all. Before implementing your button in AppDelegate
, you should know that using touchUpInside
can be tricky.
This is because user gestures are captured by View Controllers. So even if you add a button to the top of a presented view, its actions cannot be captured in AppDelegate
(or SceneDelegate
).
Both
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
and
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
are called before the views are loaded. So adding your button should be delayed if you want to capture its actions. This can be done thanks to the following method:
DispatchQueue.main.async
After its implementation, your button's actions should be accessible just the same way as if you have prepared it in View Controller.
Of course, DispatchQueue
can be tricky, so the best way in my opinion is to test your functionality on real devices.
This is it, and thanks for reading!