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)
|
.navigationBarTitle(Text("Photos"), displayMode: .inline)
|
||||||
.backport.toolbar {
|
.backport.toolbar {
|
||||||
Backport.ToolbarItem(placement: .primaryAction) {
|
Backport.ToolbarItem(placement: .primaryAction) {
|
||||||
Text("Done")
|
Button("Add") {
|
||||||
|
|
||||||
|
}
|
||||||
|
.font(.body.weight(.semibold))
|
||||||
|
.disabled(selection.isEmpty)
|
||||||
|
.opacity(maxSelection == 1 ? 0 : 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
Backport.ToolbarItem(placement: .cancellationAction) {
|
Backport.ToolbarItem(placement: .cancellationAction) {
|
||||||
Text("Cancel")
|
Button("Cancel") {
|
||||||
|
selection = []
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Backport.ToolbarItem(placement: .principal) {
|
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()
|
.backport.interactiveDismissDisabled()
|
||||||
}
|
.backport.onChange(of: source) { newValue in
|
||||||
|
selection = source == .albums ? [.init(itemIdentifier: "")] : []
|
||||||
private var picker: some View {
|
|
||||||
Picker("", selection: $source) {
|
|
||||||
ForEach(Source.allCases) { source in
|
|
||||||
Text(source.rawValue)
|
|
||||||
.tag(source)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.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
|
#endif
|
||||||
|
|
|
@ -9,6 +9,7 @@ public extension Backport<Any> {
|
||||||
case cancellationAction
|
case cancellationAction
|
||||||
case destructiveAction
|
case destructiveAction
|
||||||
case principal
|
case principal
|
||||||
|
case status
|
||||||
|
|
||||||
var isLeading: Bool {
|
var isLeading: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -30,16 +31,18 @@ public extension Backport<Any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ToolbarItem: View {
|
struct ToolbarItem: View {
|
||||||
|
let id: String
|
||||||
let placement: Backport.ToolbarItemPlacement
|
let placement: Backport.ToolbarItemPlacement
|
||||||
let content: AnyView
|
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.placement = placement
|
||||||
self.content = AnyView(content())
|
self.content = AnyView(content())
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
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)
|
@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
|
@ViewBuilder
|
||||||
func toolbar(@BackportToolbarContentBuilder _ items: () -> [Backport<Any>.ToolbarItem]) -> some View {
|
private var leading: some View {
|
||||||
let items = items()
|
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
|
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 { controller in
|
||||||
controller?.navigationItem.titleView = UIHostingController(
|
if !principalItems.isEmpty {
|
||||||
rootView: items.filter { $0.placement == .principal }.content,
|
controller?.navigationItem.titleView = UIHostingController(
|
||||||
ignoreSafeArea: false
|
rootView: principal,
|
||||||
).view
|
ignoreSafeArea: false
|
||||||
|
).view
|
||||||
|
}
|
||||||
|
|
||||||
|
if !statusItems.isEmpty {
|
||||||
|
controller?.navigationController?.setToolbarHidden(false, animated: false)
|
||||||
|
controller?.toolbarItems = [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@resultBuilder
|
@available(iOS, introduced: 13, deprecated: 14)
|
||||||
public struct BackportToolbarContentBuilder { }
|
public extension Backport where Wrapped: View {
|
||||||
public extension BackportToolbarContentBuilder {
|
@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] {
|
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] {
|
static func buildBlock(_ content: Backport<Any>.ToolbarItem) -> [Backport<Any>.ToolbarItem] {
|
||||||
|
|
Loading…
Reference in New Issue