Introduces rudimentary Toolbar support for iOS 13
This commit is contained in:
parent
cce0b59d3d
commit
b53b53e9b0
|
@ -28,66 +28,48 @@ internal struct PhotosPickerView: View {
|
|||
.navigationBarTitle(Text("Photos"), displayMode: .inline)
|
||||
.backport.toolbar {
|
||||
Backport.ToolbarItem(placement: .primaryAction) {
|
||||
Text("Done")
|
||||
Button("Add") {
|
||||
|
||||
}
|
||||
.font(.body.weight(.semibold))
|
||||
.disabled(selection.isEmpty)
|
||||
.opacity(maxSelection == 1 ? 0 : 1)
|
||||
}
|
||||
|
||||
Backport.ToolbarItem(placement: .cancellationAction) {
|
||||
Text("Cancel")
|
||||
Button("Cancel") {
|
||||
selection = []
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
Backport.ToolbarItem(placement: .principal) {
|
||||
Text("Principal")
|
||||
Picker("", selection: $source) {
|
||||
ForEach(Source.allCases) { source in
|
||||
Text(source.rawValue)
|
||||
.tag(source)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.fixedSize()
|
||||
}
|
||||
|
||||
Backport.ToolbarItem(placement: .status) {
|
||||
VStack {
|
||||
Text(selection.isEmpty ? "Select Items" : "Selected (\(selection.count))")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
|
||||
Text("Select up to \(maxSelection ?? 1) items.")
|
||||
.foregroundColor(.secondary)
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
}
|
||||
// .backport.toolbar {
|
||||
// picker
|
||||
// } leading: {
|
||||
// Button("Cancel") {
|
||||
// dismiss()
|
||||
// }
|
||||
// } trailing: {
|
||||
// Button("Add") {
|
||||
//
|
||||
// }
|
||||
// .font(.body.weight(.semibold))
|
||||
// .disabled(selection.isEmpty)
|
||||
// .opacity(maxSelection == 1 ? 0 : 1)
|
||||
// }
|
||||
// .controller { controller in
|
||||
// if #available(iOS 14, *) { } else {
|
||||
// guard controller?.navigationItem.titleView == nil else { return }
|
||||
// controller?.navigationItem.titleView = UIHostingController(rootView: picker, ignoreSafeArea: false).view
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.backport.interactiveDismissDisabled()
|
||||
}
|
||||
|
||||
private var picker: some View {
|
||||
Picker("", selection: $source) {
|
||||
ForEach(Source.allCases) { source in
|
||||
Text(source.rawValue)
|
||||
.tag(source)
|
||||
}
|
||||
.backport.onChange(of: source) { newValue in
|
||||
selection = source == .albums ? [.init(itemIdentifier: "")] : []
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
|
||||
//private extension Backport where Wrapped: View {
|
||||
// @ViewBuilder
|
||||
// func toolbar<Leading: View, Trailing: View, Principal: View>(@ViewBuilder principal: () -> Principal, @ViewBuilder leading: () -> Leading, @ViewBuilder trailing: () -> Trailing) -> some View {
|
||||
// if #available(iOS 14, *) {
|
||||
// content.toolbar {
|
||||
//// ToolbarItem(placement: .navigationBarLeading, content: leading)
|
||||
//// ToolbarItem(placement: .navigationBarTrailing, content: trailing)
|
||||
//// ToolbarItem(placement: .principal, content: principal)
|
||||
// }
|
||||
// } else {
|
||||
// content
|
||||
// .navigationBarItems(leading: leading(), trailing: trailing())
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
#endif
|
||||
|
|
|
@ -9,6 +9,7 @@ public extension Backport<Any> {
|
|||
case cancellationAction
|
||||
case destructiveAction
|
||||
case principal
|
||||
case status
|
||||
|
||||
var isLeading: Bool {
|
||||
switch self {
|
||||
|
@ -30,16 +31,18 @@ public extension Backport<Any> {
|
|||
}
|
||||
|
||||
struct ToolbarItem: View {
|
||||
let id: String
|
||||
let placement: Backport.ToolbarItemPlacement
|
||||
let content: AnyView
|
||||
|
||||
public init<Content: View>(placement: Backport.ToolbarItemPlacement, @ViewBuilder content: () -> Content) {
|
||||
public init<Content: View>(id: String = UUID().uuidString, placement: Backport.ToolbarItemPlacement = .automatic, @ViewBuilder content: () -> Content) {
|
||||
self.id = id
|
||||
self.placement = placement
|
||||
self.content = AnyView(content())
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
content
|
||||
content.id(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,26 +60,101 @@ extension Collection where Element == Backport<Any>.ToolbarItem, Indices: Random
|
|||
}
|
||||
|
||||
@available(iOS, introduced: 13, deprecated: 14)
|
||||
public extension Backport where Wrapped: View {
|
||||
struct ToolbarModifier: ViewModifier {
|
||||
let leadingItems: [Backport<Any>.ToolbarItem]
|
||||
let trailingItems: [Backport<Any>.ToolbarItem]
|
||||
let principalItems: [Backport<Any>.ToolbarItem]
|
||||
let statusItems: [Backport<Any>.ToolbarItem]
|
||||
|
||||
init(items: [Backport<Any>.ToolbarItem]) {
|
||||
leadingItems = items.filter { $0.placement.isLeading }
|
||||
trailingItems = items.filter { $0.placement.isTrailing }
|
||||
principalItems = items.filter { $0.placement == .principal }
|
||||
statusItems = items.filter { $0.placement == .status }
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func toolbar(@BackportToolbarContentBuilder _ items: () -> [Backport<Any>.ToolbarItem]) -> some View {
|
||||
let items = items()
|
||||
private var leading: some View {
|
||||
if !leadingItems.isEmpty {
|
||||
HStack {
|
||||
ForEach(leadingItems, id: \.id) { item in
|
||||
item.content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var trailing: some View {
|
||||
if !trailingItems.isEmpty {
|
||||
HStack {
|
||||
ForEach(trailingItems, id: \.id) { item in
|
||||
item.content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var principal: some View {
|
||||
if !principalItems.isEmpty {
|
||||
HStack {
|
||||
ForEach(principalItems, id: \.id) { item in
|
||||
item.content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var status: some View {
|
||||
if !statusItems.isEmpty {
|
||||
HStack {
|
||||
ForEach(statusItems, id: \.id) { item in
|
||||
item.content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Namespace private var namespace
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.navigationBarItems(leading: items.filter { $0.placement.isLeading }.content, trailing: items.filter { $0.placement.isTrailing }.content)
|
||||
.navigationBarItems(leading: leading, trailing: trailing)
|
||||
.controller { controller in
|
||||
controller?.navigationItem.titleView = UIHostingController(
|
||||
rootView: items.filter { $0.placement == .principal }.content,
|
||||
ignoreSafeArea: false
|
||||
).view
|
||||
if !principalItems.isEmpty {
|
||||
controller?.navigationItem.titleView = UIHostingController(
|
||||
rootView: principal,
|
||||
ignoreSafeArea: false
|
||||
).view
|
||||
}
|
||||
|
||||
if !statusItems.isEmpty {
|
||||
controller?.navigationController?.setToolbarHidden(false, animated: false)
|
||||
controller?.toolbarItems = [
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@resultBuilder
|
||||
public struct BackportToolbarContentBuilder { }
|
||||
public extension BackportToolbarContentBuilder {
|
||||
@available(iOS, introduced: 13, deprecated: 14)
|
||||
public extension Backport where Wrapped: View {
|
||||
@ViewBuilder
|
||||
func toolbar(@Backport<Any>.ToolbarContentBuilder _ items: () -> [Backport<Any>.ToolbarItem]) -> some View {
|
||||
content.modifier(ToolbarModifier(items: items()))
|
||||
}
|
||||
}
|
||||
|
||||
public extension Backport<Any> {
|
||||
@resultBuilder struct ToolbarContentBuilder { }
|
||||
}
|
||||
|
||||
public extension Backport<Any>.ToolbarContentBuilder {
|
||||
static func buildBlock() -> [Backport<Any>.ToolbarItem] {
|
||||
[Backport<Any>.ToolbarItem(placement: .automatic, content: { EmptyView() })]
|
||||
[.init(content: EmptyView.init)]
|
||||
}
|
||||
|
||||
static func buildBlock(_ content: Backport<Any>.ToolbarItem) -> [Backport<Any>.ToolbarItem] {
|
||||
|
|
Loading…
Reference in New Issue