Skip to content

Instantly share code, notes, and snippets.

@skl
Created June 19, 2020 01:08
Show Gist options
  • Save skl/db94520af2dd1317220c5eadb9de81da to your computer and use it in GitHub Desktop.
Save skl/db94520af2dd1317220c5eadb9de81da to your computer and use it in GitHub Desktop.
This is a workaround for iPad ActionSheets disappearing off the screen.
//
// PopSheet.swift
//
// Adapted from:
// https://stackoverflow.com/questions/56910941/present-actionsheet-in-swiftui-on-ipad
//
import SwiftUI
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(.bottom), arrowEdge: arrowEdge, content: { content().popover(isPresented: isPresented) })
} else {
actionSheet(isPresented: isPresented, content: { content().actionSheet() })
}
}
}
}
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 {
let width = UIScreen.main.bounds.width / 2
let maxHeight = UIScreen.main.bounds.height * 0.75
var height = CGFloat(buttons.count * 40) + 140 // buttons + title
if message != nil {
height += 45
}
if height > maxHeight {
height = maxHeight
}
return VStack() {
title.font(.title)
if message != nil {
message.font(.body).padding()
}
Divider()
List {
ForEach(buttons) { button in
SwiftUI.Button(action: {
// hide the popover whenever an action is performed
isPresented.wrappedValue = false
// another bug: if the action shows a sheet or popover, it will fail unless this one has already been dismissed
DispatchQueue.main.async {
button.action?()
}
}, label: {
button.label
})
.padding()
.frame(maxWidth: .infinity)
}
}
}
.padding()
.frame(width: width, height: height)
}
public struct Button: Identifiable {
let id = UUID().uuidString
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)
}
}
}
@skl
Copy link
Author

skl commented Jun 19, 2020

Usage

Before:

.actionSheet(isPresented: $isShowingLogoutSheet) {
    ActionSheet(
        title: Text("Would you like to log out?"),
        message: Text("Please choose an option"),
        buttons: [
            .default(Text("Log Out")) {
                // Execute your logout() function here
            },
            .cancel()
        ])

After:

.popSheet(isPresented: $isShowingLogoutSheet) {
    PopSheet(
        title: Text("Would you like to log out?"),
        message: Text("Please choose an option"),
        buttons: [
            PopSheet.Button(kind: .default, label: Text("Log Out"), action: {
                // Execute your logout() function here
            }),
            PopSheet.Button(kind: .cancel, label: Text("Cancel"), action: {})
        ])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment