[Solution] SwiftUI ActionSheet crash on iPad

2019-11-29 22:06:29.237883+1300 Instafilter[651:58326] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIAlertController (<UIAlertController: 0x102008000>) of style UIAlertControllerStyleActionSheet from _TtGC7SwiftUI19UIHostingControllerV11Instafilter11ContentView_ (<_TtGC7SwiftUI19UIHostingControllerV11Instafilter11ContentView_: 0x101508c20>). The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem.  If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'*** First throw call stack:(0x1a51e8a48 0x1a4f0ffa4 0x1a8b5b588 0x1a8b657e0 0x1a8b63324 0x1a92644c0 0x1a9254010 0x1a9283e60 0x1a5165e68 0x1a5160d54 0x1a5161320 0x1a5160adc 0x1af0e9328 0x1a925ae78 0x100f726cc 0x1a4fea360)libc++abi.dylib: terminating with uncaught exception of type NSException(lldb)
  1. Implement a PopSheet
struct PopSheet {
let title: Text
let message: Text?
let buttons: [PopSheet.Button]

public init(title: Text, message: Text? = nil, buttons: [PopSheet.Button] = [.cancel()]) {
self.title = title
self.message = message
self.buttons = buttons
}


func actionSheet() -> ActionSheet {
ActionSheet(title: title, message: message, buttons: buttons.map({ popButton in
switch popButton.kind {
case .default: return .default(popButton.label, action: popButton.action)
case .cancel: return .cancel(popButton.label, action: popButton.action)
case .destructive: return .destructive(popButton.label, action: popButton.action)
}
}))
}

func popover(isPresented: Binding<Bool>) -> some View {
VStack {
self.title.padding(.top)
Divider()
List {
ForEach(Array(self.buttons.enumerated()), id: \.offset) { (offset, button) in
VStack {
SwiftUI.Button(action: {
isPresented.wrappedValue = false
DispatchQueue.main.async {
button.action?()
}
}, label: {
button.label.font(.subheadline)
})
}
}
}
}
}

public struct Button {
let kind: Kind
let label: Text
let action: (() -> Void)?
enum Kind { case `default`, cancel, destructive }

/// Creates a `Button` with the default style.
public static func `default`(_ label: Text, action: (() -> Void)? = {}) -> Self {
Self(kind: .default, label: label, action: action)
}

/// Creates a `Button` that indicates cancellation of some operation.
public static func cancel(_ label: Text, action: (() -> Void)? = {}) -> Self {
Self(kind: .cancel, label: label, action: action)
}

/// Creates an `Alert.Button` that indicates cancellation of some operation.
public static func cancel(_ action: (() -> Void)? = {}) -> Self {
Self(kind: .cancel, label: Text("Cancel"), action: action)
}

/// Creates an `Alert.Button` with a style indicating destruction of some data.
public static func destructive(_ label: Text, action: (() -> Void)? = {}) -> Self {
Self(kind: .destructive, label: label, action: action)
}
}
}
extension View {
func popSheet(isPresented: Binding<Bool>, arrowEdge: Edge = .bottom, content: @escaping () -> PopSheet) -> some View {
Group {
if UIDevice.current.userInterfaceIdiom == .pad {
popover(isPresented: isPresented, attachmentAnchor: .point(.topTrailing), arrowEdge: arrowEdge, content: { content().popover(isPresented: isPresented) })
} else {
actionSheet(isPresented: isPresented, content: { content().actionSheet() })
}
}
}
}
Button(self.filterName) {
self.showingFilterSheet = true
}
.popSheet(isPresented: self.$showingFilterSheet, content: {
PopSheet(title: Text("Select a filter"), buttons: [
PopSheet.Button(kind: .default, label: Text(FilterType.Crystallize.rawValue), action: {
self.filterName = FilterType.Crystallize.rawValue
self.setFilter(CIFilter.crystallize())
}),
PopSheet.Button(kind: .default, label: Text(FilterType.Edges.rawValue), action: {
self.filterName = FilterType.Edges.rawValue
self.setFilter(CIFilter.edges())
}),
PopSheet.Button(kind: .default, label: Text(FilterType.GaussianBlur.rawValue), action: {
self.filterName = FilterType.GaussianBlur.rawValue
self.setFilter(CIFilter.gaussianBlur())
}),
PopSheet.Button(kind: .default, label: Text(FilterType.SepiaTone.rawValue), action: {
self.filterName = FilterType.SepiaTone.rawValue
self.setFilter(CIFilter.sepiaTone())
}),
PopSheet.Button(kind: .default, label: Text(FilterType.UnsharpMask.rawValue), action: {
self.filterName = FilterType.UnsharpMask.rawValue
self.setFilter(CIFilter.unsharpMask())
}),
PopSheet.Button(kind: .default, label: Text(FilterType.Vignette.rawValue), action: {
self.filterName = FilterType.Vignette.rawValue
self.setFilter(CIFilter.vibrance())
}),
PopSheet.Button(kind: .cancel, label: Text("Cancel"), action: {})
])
}

--

--

--

Senior mobile Developer (https://github.com/liemvo)

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Using CircleCI and Kubernetes to achieve seamless deployments to Google Container Engine

How to secure AWS Api Gateway Requests with Signature Version 4 using AWS Amplify?

New Release: Bento application updates 3.1 & 3.2

Decentralized Video On LBRY — Julian Chandra

[Programming!] Class??Object??…new!

Production Release Highlights: August 12th, 2021

Raw Metal

Spring 2020 CS373 : SeongBeom Ko

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Liem Vo

Liem Vo

Senior mobile Developer (https://github.com/liemvo)

More from Medium

Guide to manage secrets in SwiftUI app

New configuration file

SwiftUI. How To pass Binding variable to PreviewProvider

Building custom Property Wrappers in Swift

Why can Double() store a larger # than Int() ?