Global alert in SwiftUI - swiftui

I'm trying to present a global alert in SwiftUI. This alert should be displayed on top of everything regardless of what it is currently displayed / presented on screen (a sheet for example).
This is my code:
#main
struct MyApp: App {
#State private var showAlert = false
var body: some Scene {
WindowGroup {
MainView()
.onReceive(NotificationCenter.default.publisher(for:NSNotification.Name.SomeNotification), perform: { _ in
showAlert = true
})
.alert(
isPresented: $showAlert,
content: {Alert(title: Text("Alert!"))}
)
}
}
}
This in some cases will not work, for example if the notification is received when a sheet is currently presented on screen. In this case the alert is not displayed and the following message is displayed on the console:
Blockquote
[Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x7fbee6921400> on <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x7fbee642ac60> (from <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x7fbee642ac60>) which is already presenting <TtGC7SwiftUI22SheetHostingControllerVS_7AnyView: 0x7fbee8405360>.
This make sense because I'm trying to present an alert on a view that is already presenting a sheet.
On UIKit I achieved this using the following class:
class GlobalAlertController: UIAlertController {
var globalPresentationWindow: UIWindow?
func presentGlobally(animated: Bool, completion: (() -> Void)?) {
globalPresentationWindow = UIWindow(frame: UIScreen.main.bounds)
globalPresentationWindow?.rootViewController = UIViewController()
globalPresentationWindow?.windowLevel = UIWindow.Level.alert + 1
globalPresentationWindow?.backgroundColor = .clear
globalPresentationWindow?.makeKeyAndVisible()
globalPresentationWindow?.rootViewController?.present(self, animated: animated, completion: completion)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
globalPresentationWindow?.isHidden = true
globalPresentationWindow = nil
}
}
This class allows me to display a global alert on top of everything in this way:
let alertController = GlobalAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Done", style: .cancel, handler: nil))
alertController.presentGlobally(animated: true, completion: nil)
Anyone know how to implement something like that in SwiftUI?

Just found that I can in fact use my old UIKit code to achieve this. The only thing that need to be changed is adding support for scenes (SwiftUI use scenes by design), like this:
class GlobalAlertController: UIAlertController {
var globalPresentationWindow: UIWindow?
func presentGlobally(animated: Bool, completion: (() -> Void)?) {
globalPresentationWindow = UIWindow(frame: UIScreen.main.bounds)
//This is needed when using scenes.
if let currentWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
globalPresentationWindow?.windowScene = currentWindowScene
}
globalPresentationWindow?.rootViewController = UIViewController()
globalPresentationWindow?.windowLevel = UIWindow.Level.alert + 1
globalPresentationWindow?.backgroundColor = .clear
globalPresentationWindow?.makeKeyAndVisible()
globalPresentationWindow?.rootViewController?.present(self, animated: animated, completion: completion)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
globalPresentationWindow?.isHidden = true
globalPresentationWindow = nil
}
}
Now I can just display the global alert like this:
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
MainView()
.onReceive(NotificationCenter.default.publisher(for:NSNotification.Name.SomeNotification), perform: { _ in
let alertController = GlobalAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Done", style: .cancel, handler: nil))
alertController.presentGlobally(animated: true, completion: nil)
})
}
}
}
It works, although a more SwiftUI like approach would be nice.

Related

How to make UIViewControllerRepresentable ViewController reusable in many SwiftUI views?

I have managed to create a UIKit UIAlertController and present it to my SwiftUI View. I had to create a UIKit UIAlertController so i can be able to change the colors of the cancel button and submit button as well as make the submit button bold. It worked but now i need to use the UIAlertController in other SwiftUI Views so i have to make it reusable.
How can i make UIViewControllerRepresentable ViewController reusable in many SwiftUI views without having to copy and past the code below? I am thinking of using a class and just instantiate the class and use the UIKit UIAlertController. Any help or small example appreciated.
This is the UIViewControllerRepresentable struct in my SwiftUI view. The code below has to be reusable without having to add it in each SwiftUI view.
struct UIAlertViewPopup: UIViewControllerRepresentable {
#Binding var show: Bool
let viewModel: SettingsViewModel
var title: String
var message: String
func makeUIViewController(context: UIViewControllerRepresentableContext<UIAlertViewPopup>) -> some UIViewController {
return UIViewController()
}
//update UIKit from SwiftUI
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<UIAlertViewPopup>) {
if (self.show) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive) { (action) in
self.show = false
}
let submitAction = UIAlertAction(title: "Edit profile", style: .default) { (action) in
viewModel(perform: .editProfile)
self.show = false
}
submitAction.setValue(UIColor.accentBlue, forKey: "titleTextColor")
cancelAction.setValue(UIColor.accentPrimary, forKey: "titleTextColor")
alert.addAction(cancelAction)
alert.addAction(submitAction)
alert.preferredAction = submitAction
DispatchQueue.main.async {
uiViewController.present(alert, animated: true, completion: {
self.show = false
})
}
}
}
//update SwiftUI from UIKit through delegates
func makeCoordinator() -> UIAlertViewPopup.Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIAlertViewDelegate {
var control: UIAlertViewPopup
init(_ control: UIAlertViewPopup) {
self.control = control
}
}
}
I use it in my SwiftUI view like this
#State var isAlertVisible: Bool = false
Button {
isAlertVisible = true
}
then on the .background modifier
.background(UIAlertViewPopup(show: $viewModel.isAlertVisible, viewModel: viewModel, title: "", message: "hello from UIKit alert"))

SwiftUI Search Bar in line with navigation bar

Does anyone have working Swiftui code that will produce a search bar in the navigation bar that is inline with the back button? As if it is a toolbar item.
Currently I have code that will produce a search bar below the navigation back button but would like it in line like the picture attached shows (where the "hi" is):
I am using code that I found in an example:
var body: some View {
let shopList = genShopList(receiptList: allReceipts)
VStack{
}
.navigationBarSearch(self.$searchInput)
}
public extension View {
public func navigationBarSearch(_ searchText: Binding<String>) -> some View {
return overlay(SearchBar(text: searchText)
.frame(width: 0, height: 0))
}
}
fileprivate struct SearchBar: UIViewControllerRepresentable {
#Binding
var text: String
init(text: Binding<String>) {
self._text = text
}
func makeUIViewController(context: Context) -> SearchBarWrapperController {
return SearchBarWrapperController()
}
func updateUIViewController(_ controller: SearchBarWrapperController, context: Context) {
controller.searchController = context.coordinator.searchController
}
func makeCoordinator() -> Coordinator {
return Coordinator(text: $text)
}
class Coordinator: NSObject, UISearchResultsUpdating {
#Binding
var text: String
let searchController: UISearchController
private var subscription: AnyCancellable?
init(text: Binding<String>) {
self._text = text
self.searchController = UISearchController(searchResultsController: nil)
super.init()
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = true
searchController.obscuresBackgroundDuringPresentation = false
self.searchController.searchBar.text = self.text
self.subscription = self.text.publisher.sink { _ in
self.searchController.searchBar.text = self.text
}
}
deinit {
self.subscription?.cancel()
}
func updateSearchResults(for searchController: UISearchController) {
guard let text = searchController.searchBar.text else { return }
self.text = text
}
}
class SearchBarWrapperController: UIViewController {
var searchController: UISearchController? {
didSet {
self.parent?.navigationItem.searchController = searchController
}
}
override func viewWillAppear(_ animated: Bool) {
self.parent?.navigationItem.searchController = searchController
}
override func viewDidAppear(_ animated: Bool) {
self.parent?.navigationItem.searchController = searchController
}
}
}
If anyone has a solution to this problem that would be greatly appreciated! I know that in IoS 15 they are bringing out .searchable but looking for something that will work for earlier versions too.
You can put any control in the position you want by using the .toolbar modifier (iOS 14+) and an item with .principal placement, e.g.:
var body: some View {
VStack {
// rest of view
}
.toolbar {
ToolbarItem(placement: .principal) {
MySearchField(text: $searchText)
}
}
}
A couple of things to note:
The principal position overrides an inline navigation title, either when it's set with .navigationBarTitleDisplayMode(.inline) or when you have a large title and scroll up the page.
It's possible that your custom view expands horizontally so much that the back button loses any text component. Here, I used a TextField to illustrate the point:
You might be able to mitigate for that by assigning a maximum width with .frame(maxWidth:), but at the very least it's something to be aware of.

Display Share Sheet (UIActivityViewController) in a Modal .sheet() in SwiftUI [duplicate]

I'm trying to present a UIActivityViewController (share sheet) from a SwiftUI View. I created a view called ShareSheet conformed to UIViewControllerRepresentable to configure the UIActivityViewController, but it's turning out to be not as trivial to actually present this.
struct ShareSheet: UIViewControllerRepresentable {
typealias UIViewControllerType = UIActivityViewController
var sharing: [Any]
func makeUIViewController(context: UIViewControllerRepresentableContext<ShareSheet>) -> UIActivityViewController {
UIActivityViewController(activityItems: sharing, applicationActivities: nil)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ShareSheet>) {
}
}
Doing so naively via .sheet leads to the following.
.sheet(isPresented: $showShareSheet) {
ShareSheet(sharing: [URL(string: "https://example.com")!])
}
Is there a way to present this like it's usually presented? As in covering half the screen?
Hope this will help you,
struct ShareSheetView: View {
var body: some View {
Button(action: actionSheet) {
Image(systemName: "square.and.arrow.up")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 36, height: 36)
}
}
func actionSheet() {
guard let data = URL(string: "https://www.apple.com") else { return }
let av = UIActivityViewController(activityItems: [data], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
}
}
In iOS 14, Swift 5, Xcode 12.5 at least, I was able to accomplish this fairly easily by simply wrapping the UIActivityViewController in another view controller. It doesn't require inspecting the view hierarchy or using any 3rd party libraries. The only hackish part is asynchronously presenting the view controller, which might not even be necessary. Someone with more SwiftUI experience might be able to offer suggestions for improvement.
import Foundation
import SwiftUI
import UIKit
struct ActivityViewController: UIViewControllerRepresentable {
#Binding var shareURL: URL?
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> some UIViewController {
let containerViewController = UIViewController()
return containerViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
guard let shareURL = shareURL, context.coordinator.presented == false else { return }
context.coordinator.presented = true
let activityViewController = UIActivityViewController(activityItems: [shareURL], applicationActivities: nil)
activityViewController.completionWithItemsHandler = { activity, completed, returnedItems, activityError in
self.shareURL = nil
context.coordinator.presented = false
if completed {
// ...
} else {
// ...
}
}
// Executing this asynchronously might not be necessary but some of my tests
// failed because the view wasn't yet in the view hierarchy on the first pass of updateUIViewController
//
// There might be a better way to test for that condition in the guard statement and execute this
// synchronously if we can be be sure updateUIViewController is invoked at least once after the view is added
DispatchQueue.main.asyncAfter(deadline: .now()) {
uiViewController.present(activityViewController, animated: true)
}
}
class Coordinator: NSObject {
let parent: ActivityViewController
var presented: Bool = false
init(_ parent: ActivityViewController) {
self.parent = parent
}
}
}
struct ContentView: View {
#State var shareURL: URL? = nil
var body: some View {
ZStack {
Button(action: { shareURL = URL(string: "https://apple.com") }) {
Text("Share")
.foregroundColor(.white)
.padding()
}
.background(Color.blue)
if shareURL != nil {
ActivityViewController(shareURL: $shareURL)
}
}
.frame(width: 375, height: 812)
}
}
iOS 15 / Swift 5 / Xcode 13
Extension to get the top presented UIViewController:
import UIKit
extension UIApplication {
// MARK: No shame!
static func TopPresentedViewController() -> UIViewController? {
guard let rootViewController = UIApplication.shared
.connectedScenes.lazy
.compactMap({ $0.activationState == .foregroundActive ? ($0 as? UIWindowScene) : nil })
.first(where: { $0.keyWindow != nil })?
.keyWindow?
.rootViewController
else {
return nil
}
var topController = rootViewController
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
}
Then use it to present your UIActivityViewController:
UIApplication.TopPresentedViewController?.present(activityViewController, animated: true, completion: nil)
Original Answer (deprecated code):
It's not pretty but you can call it directly like this (considering your app has only 1 window):
UIApplication.shared.windows.first?.rootViewController?.present(activityViewController, animated: true, completion: nil)
And if you get some warning blablabla:
Warning: Attempt to present ... which is already presenting ...
you can do something like this to get the top most view controller and call present on it.
There's a UIModalPresentationStyle which can be used to display certain presentations:
case pageSheet
A presentation style that partially covers the underlying content.
The way you apply the presentation style:
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
let v = UIActivityViewController(activityItems: sharing, applicationActivities: nil)
v.modalPresentationStyle = .pageSheet
return v
}
A list of the Presentations can be found here:
https://developer.apple.com/documentation/uikit/uimodalpresentationstyle
I haven't yet tested them all myself so I apologise in advance if this didn't end up working like you expected it to.
Alternatively you can have a look at this answer where they mention a third-party library, which will allow you to create a half modal in the way that it's usually presented.

MFMessageComposeViewController + SwiftUI Buggy Behavior

I'm using ViewControllerRepresentable to present a MFMessageComposeViewController so users can send texts from my app.
However, every time the view is presented, it's very buggy - elements randomly disappear, scrolling is off, and the screen flickers. Tested on iOS 14.2 and 14.3.
Here's the code:
import SwiftUI
import MessageUI
struct MessageView: UIViewControllerRepresentable {
var recipient: String
class Coordinator: NSObject, MFMessageComposeViewControllerDelegate {
var completion: () -> Void
init(completion: #escaping ()->Void) {
self.completion = completion
}
// delegate method
func messageComposeViewController(_ controller: MFMessageComposeViewController,
didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
completion()
}
}
func makeCoordinator() -> Coordinator {
return Coordinator() {} // not using completion handler
}
func makeUIViewController(context: Context) -> MFMessageComposeViewController {
let vc = MFMessageComposeViewController()
vc.recipients = [recipient]
vc.messageComposeDelegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: MFMessageComposeViewController, context: Context) {}
typealias UIViewControllerType = MFMessageComposeViewController
}
and my view
struct ContentView: View {
#State private var isShowingMessages = false
#State var result: Result<MFMailComposeResult, Error>? = nil
var body: some View {
VStack {
Button("Show Messages") {
self.isShowingMessages = true
}
.sheet(isPresented: self.$isShowingMessages) {
MessageView(recipient: "+15555555555")
}
.edgesIgnoringSafeArea(.bottom)
}
}
}
Is there something wrong with the way I'm presenting this view? Has anyone else experienced this behavior? Similar behavior happens with MFMailComposeViewController, but it's not as buggy.
5 minutes later, I realized I needed to add this when presenting the sheet:
MessageView(recipient: "+15555555555")
.ignoresSafeArea()
The view looked buggy because it was trying to account for the keyboard safe area and had a hard time doing it.

SwiftUI - PresentationButton with modal that is full screen

I am trying to implement a button that presents another scene with a "Slide from Botton" animation.
PresentationButton looked like a good candidate, so I gave it a try:
import SwiftUI
struct ContentView : View {
var body: some View {
NavigationView {
PresentationButton(destination: Green().frame(width: 1000.0)) {
Text("Click")
}.navigationBarTitle(Text("Navigation"))
}
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
Group {
ContentView()
.previewDevice("iPhone X")
.colorScheme(.dark)
ContentView()
.colorScheme(.dark)
.previewDevice("iPad Pro (12.9-inch) (3rd generation)"
)
}
}
}
#endif
And here is the result:
I want the green view to cover the whole screen, and also the modal to be not "draggable to close".
Is it possible to add modifier to PresentationButton to make it full screen, and not draggable?
I have also tried a Navigation Button, but:
- It doesn't "slide from bottom"
- It creates a "back button" on detail view, which I don't want
thanks!
Unfortunately, as of Beta 2 Beta 3, this is not possible in pure SwiftUI. You can see that Modal has no parameters for anything like UIModalPresentationStyle.fullScreen. Likewise for PresentationButton.
I suggest filing a radar.
The nearest you can currently do is something like:
#State var showModal: Bool = false
var body: some View {
NavigationView {
Button(action: {
self.showModal = true
}) {
Text("Tap me!")
}
}
.navigationBarTitle(Text("Navigation!"))
.overlay(self.showModal ? Color.green : nil)
}
Of course, from there you can add whatever transition you like in the overlay.
Although my other answer is currently correct, people probably want to be able to do this now. We can use the Environment to pass a view controller to children. Gist here
struct ViewControllerHolder {
weak var value: UIViewController?
}
struct ViewControllerKey: EnvironmentKey {
static var defaultValue: ViewControllerHolder { return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController ) }
}
extension EnvironmentValues {
var viewController: UIViewControllerHolder {
get { return self[ViewControllerKey.self] }
set { self[ViewControllerKey.self] = newValue }
}
}
Add an extension to UIViewController
extension UIViewController {
func present<Content: View>(style: UIModalPresentationStyle = .automatic, #ViewBuilder builder: () -> Content) {
// Must instantiate HostingController with some sort of view...
let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
toPresent.modalPresentationStyle = style
// ... but then we can reset rootView to include the environment
toPresent.rootView = AnyView(
builder()
.environment(\.viewController, ViewControllerHolder(value: toPresent))
)
self.present(toPresent, animated: true, completion: nil)
}
}
And whenever we need it, use it:
struct MyView: View {
#Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder
private var viewController: UIViewController? {
self.viewControllerHolder.value
}
var body: some View {
Button(action: {
self.viewController?.present(style: .fullScreen) {
MyView()
}
}) {
Text("Present me!")
}
}
}
[EDIT] Although it would be preferable to do something like #Environment(\.viewController) var viewController: UIViewController? this leads to a retain cycle. Therefore, you need to use the holder.
Xcode 12.0 - SwiftUI 2 - iOS 14
Now possible. Use fullScreenCover() modifier.
var body: some View {
Button("Present!") {
self.isPresented.toggle()
}
.fullScreenCover(isPresented: $isPresented, content: FullScreenModalView.init)
}
Hacking With Swift
This version fixes the compile error present in XCode 11.1 as well as ensures that controller is presented in the style that is passed in.
import SwiftUI
struct ViewControllerHolder {
weak var value: UIViewController?
}
struct ViewControllerKey: EnvironmentKey {
static var defaultValue: ViewControllerHolder {
return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController)
}
}
extension EnvironmentValues {
var viewController: UIViewController? {
get { return self[ViewControllerKey.self].value }
set { self[ViewControllerKey.self].value = newValue }
}
}
extension UIViewController {
func present<Content: View>(style: UIModalPresentationStyle = .automatic, #ViewBuilder builder: () -> Content) {
let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
toPresent.modalPresentationStyle = style
toPresent.rootView = AnyView(
builder()
.environment(\.viewController, toPresent)
)
self.present(toPresent, animated: true, completion: nil)
}
}
To use this version, the code is unchanged from the previous version.
struct MyView: View {
#Environment(\.viewController) private var viewControllerHolder: UIViewController?
private var viewController: UIViewController? {
self.viewControllerHolder.value
}
var body: some View {
Button(action: {
self.viewController?.present(style: .fullScreen) {
MyView()
}
}) {
Text("Present me!")
}
}
}
My solution for this (which you can easily extend to allow other params on the presented sheets to be tweaked) is to just subclass UIHostingController
//HSHostingController.swift
import Foundation
import SwiftUI
class HSHostingControllerParams {
static var nextModalPresentationStyle:UIModalPresentationStyle?
}
class HSHostingController<Content> : UIHostingController<Content> where Content : View {
override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if let nextStyle = HSHostingControllerParams.nextModalPresentationStyle {
viewControllerToPresent.modalPresentationStyle = nextStyle
HSHostingControllerParams.nextModalPresentationStyle = nil
}
super.present(viewControllerToPresent, animated: flag, completion: completion)
}
}
use HSHostingController instead of UIHostingController in your scene delegate
like so:
// Use a HSHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
//This is the only change from the standard boilerplate
window.rootViewController = HSHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
then just tell the HSHostingControllerParams class what presentation style you want before triggering a sheet
.navigationBarItems(trailing:
HStack {
Button("About") {
HSHostingControllerParams.nextModalPresentationStyle = .fullScreen
self.showMenuSheet.toggle()
}
}
)
Passing the params via the class singleton feels a little 'dirty', but in practice - you would have to create a pretty obscure scenario for this not to work as expected.
You could mess around with environment variables and the like (as other answers have done) - but to me, the added complication isn't worth the purity.
update: see this gist for extended solution with additional capabilities
So I was struggling with that and I didn't like the overlay feature nor the ViewController wrapped version since it gave me some memory bug and I am very new to iOS and only know SwiftUI and no UIKit.
I developed credits the following with just SwiftUI which is probably what an overlay does but for my purposes it is much more flexible:
struct FullscreenModalView<Presenting, Content>: View where Presenting: View, Content: View {
#Binding var isShowing: Bool
let parent: () -> Presenting
let content: () -> Content
#inlinable public init(isShowing: Binding<Bool>, parent: #escaping () -> Presenting, #ViewBuilder content: #escaping () -> Content) {
self._isShowing = isShowing
self.parent = parent
self.content = content
}
var body: some View {
GeometryReader { geometry in
ZStack {
self.parent().zIndex(0)
if self.$isShowing.wrappedValue {
self.content()
.background(Color.primary.colorInvert())
.edgesIgnoringSafeArea(.all)
.frame(width: geometry.size.width, height: geometry.size.height)
.transition(.move(edge: .bottom))
.zIndex(1)
}
}
}
}
}
Adding an extension to View:
extension View {
func modal<Content>(isShowing: Binding<Bool>, #ViewBuilder content: #escaping () -> Content) -> some View where Content: View {
FullscreenModalView(isShowing: isShowing, parent: { self }, content: content)
}
}
Usage:
Use a custom view and pass the showModal variable as a Binding<Bool> to dismiss the modal from the view itself.
struct ContentView : View {
#State private var showModal: Bool = false
var body: some View {
ZStack {
Button(action: {
withAnimation {
self.showModal.toggle()
}
}, label: {
HStack{
Image(systemName: "eye.fill")
Text("Calibrate")
}
.frame(width: 220, height: 120)
})
}
.modal(isShowing: self.$showModal, content: {
Text("Hallo")
})
}
}
I hope this helps!
Greetings krjw