Initial `NavigationView` implementation (#130)
* Initial NavigationView implementation * Make the _ButtonProxy type more generic * Split Navigation.swift files * Move this too * Implement Navigation controls * Update progress.md * Make NavigationLinks links * Break line (wishing for Prettier for Swift) * Update Path.swift * n-th time’s the charm * Update Path.swift * Update project.pbxproj * Fixes * Hopefully fix build issues * Update Navigation.swift * Improve ColorDemo * Fixes & reverts * Fix crash * Revert "Fix crash" This reverts commitae6f13dcc9
. * Tweak rendering of demos * add todo for accessibility * Apply suggestions from @MaxDesiatov Co-authored-by: Max Desiatov <max@desiatov.com> * Update TokamakDemo.swift * Move things to Core.swift * Switch default destination to EmptyView * Fix build for macOS * Revert "Apply suggestions from @MaxDesiatov" This reverts commit73c9c3f6ac
. Co-authored-by: Max Desiatov <max@desiatov.com> Co-authored-by: Carson Katri <Carson.katri@gmail.com>
This commit is contained in:
parent
2b93f37d64
commit
ac50208447
|
@ -434,6 +434,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
|
@ -488,6 +489,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jedfox.Tokamak-Native";
|
||||
|
@ -556,9 +558,9 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_NAME = "TokamakDemo Native";
|
||||
SDKROOT = macosx;
|
||||
SUPPORTED_PLATFORMS = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
|
@ -580,9 +582,9 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_NAME = "TokamakDemo Native";
|
||||
SDKROOT = macosx;
|
||||
SUPPORTED_PLATFORMS = macosx;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -596,6 +598,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
@ -610,6 +613,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
|
|
@ -23,13 +23,14 @@ public class NSApplication: UIApplication {}
|
|||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
let window = UIWindow()
|
||||
var window: UIWindow?
|
||||
func application(
|
||||
_: UIApplication,
|
||||
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool {
|
||||
window.rootViewController = UIHostingController(rootView: TokamakDemoView())
|
||||
window.makeKeyAndVisible()
|
||||
window = UIWindow()
|
||||
window?.rootViewController = UIHostingController(rootView: TokamakDemoView())
|
||||
window?.makeKeyAndVisible()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2020 Tokamak contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
extension View {
|
||||
// FIXME: Implement
|
||||
public func navigationBarTitle<S>(_ title: S) -> some View where S: StringProtocol {
|
||||
self
|
||||
}
|
||||
|
||||
// FIXME: Implement
|
||||
public func navigationTitle<S>(_ title: S) -> some View where S: StringProtocol {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -65,11 +65,13 @@ extension Button: ParentView {
|
|||
}
|
||||
|
||||
/// This is a helper class that works around absence of "package private" access control in Swift
|
||||
public struct _ButtonProxy {
|
||||
public let subject: Button<Text>
|
||||
public struct _ButtonProxy<Label> where Label: View {
|
||||
let subject: Button<Label>
|
||||
|
||||
public init(_ subject: Button<Text>) { self.subject = subject }
|
||||
|
||||
public var label: _TextProxy { _TextProxy(subject.label) }
|
||||
public init(_ subject: Button<Label>) { self.subject = subject }
|
||||
public var action: () -> () { subject.action }
|
||||
}
|
||||
|
||||
extension _ButtonProxy where Label == Text {
|
||||
public var label: _TextProxy { _TextProxy(subject.label) }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2020 Tokamak contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Created by Jed Fox on 06/30/2020.
|
||||
//
|
||||
|
||||
public struct NavigationLink<Label, Destination>: View where Label: View, Destination: View {
|
||||
let destination: Destination
|
||||
let label: Label
|
||||
|
||||
@Environment(_navigationDestinationKey) var navigationContext
|
||||
|
||||
public init(destination: Destination, @ViewBuilder label: () -> Label) {
|
||||
self.destination = destination
|
||||
self.label = label()
|
||||
}
|
||||
|
||||
/// Creates an instance that presents `destination` when active.
|
||||
// public init(destination: Destination, isActive: Binding<Bool>, @ViewBuilder label: () -> Label)
|
||||
|
||||
/// Creates an instance that presents `destination` when `selection` is set
|
||||
/// to `tag`.
|
||||
// public init<V>(
|
||||
// destination: Destination,
|
||||
// tag: V, selection: Binding<V?>,
|
||||
// @ViewBuilder label: () -> Label
|
||||
// ) where V : Hashable
|
||||
|
||||
public var body: Never {
|
||||
neverBody("NavigationLink")
|
||||
}
|
||||
}
|
||||
|
||||
extension NavigationLink where Label == Text {
|
||||
/// Creates an instance that presents `destination`, with a `Text` label
|
||||
/// generated from a title string.
|
||||
public init<S>(_ title: S, destination: Destination) where S: StringProtocol {
|
||||
self.destination = destination
|
||||
label = Text(title)
|
||||
}
|
||||
|
||||
/// Creates an instance that presents `destination` when active, with a
|
||||
/// `Text` label generated from a title string.
|
||||
// public init<S>(
|
||||
// _ title: S, destination: Destination,
|
||||
// isActive: Binding<Bool>
|
||||
// ) where S : StringProtocol
|
||||
|
||||
/// Creates an instance that presents `destination` when `selection` is set
|
||||
/// to `tag`, with a `Text` label generated from a title string.
|
||||
// public init<S, V>(
|
||||
// _ title: S, destination: Destination,
|
||||
// tag: V, selection: Binding<V?>
|
||||
// ) where S : StringProtocol, V : Hashable
|
||||
}
|
||||
|
||||
/// This is a helper class that works around absence of "package private" access control in Swift
|
||||
public struct _NavigationLinkProxy<Label, Destination> where Label: View, Destination: View {
|
||||
public let subject: NavigationLink<Label, Destination>
|
||||
|
||||
public init(_ subject: NavigationLink<Label, Destination>) { self.subject = subject }
|
||||
|
||||
public var label: Label { subject.label }
|
||||
|
||||
public func activate() {
|
||||
subject.navigationContext!.wrappedValue = AnyView(subject.destination)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2020 Tokamak contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Created by Jed Fox on 06/30/2020.
|
||||
//
|
||||
|
||||
public struct NavigationView<Content>: View where Content: View {
|
||||
let content: Content
|
||||
|
||||
@State var destination = AnyView(EmptyView())
|
||||
|
||||
public init(@ViewBuilder content: () -> Content) {
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
neverBody("NavigationView")
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a helper class that works around absence of "package private" access control in Swift
|
||||
public struct _NavigationViewProxy<Content: View> {
|
||||
public let subject: NavigationView<Content>
|
||||
|
||||
public init(_ subject: NavigationView<Content>) { self.subject = subject }
|
||||
|
||||
public var content: Content { subject.content }
|
||||
public var body: some View {
|
||||
HStack {
|
||||
content
|
||||
subject.destination
|
||||
}.environment(\.navigationDestination, subject.$destination)
|
||||
}
|
||||
}
|
||||
|
||||
struct NavigationDestinationKey: EnvironmentKey {
|
||||
public static let defaultValue: Binding<AnyView>? = nil
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var navigationDestination: Binding<AnyView>? {
|
||||
get {
|
||||
self[NavigationDestinationKey.self]
|
||||
}
|
||||
set {
|
||||
self[NavigationDestinationKey.self] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public let _navigationDestinationKey = \EnvironmentValues.navigationDestination
|
|
@ -83,6 +83,8 @@ public typealias HStack = TokamakCore.HStack
|
|||
public typealias LazyHGrid = TokamakCore.LazyHGrid
|
||||
public typealias LazyVGrid = TokamakCore.LazyVGrid
|
||||
public typealias List = TokamakCore.List
|
||||
public typealias NavigationLink = TokamakCore.NavigationLink
|
||||
public typealias NavigationView = TokamakCore.NavigationView
|
||||
public typealias OutlineGroup = TokamakCore.OutlineGroup
|
||||
public typealias Picker = TokamakCore.Picker
|
||||
public typealias ScrollView = TokamakCore.ScrollView
|
||||
|
@ -112,6 +114,8 @@ public typealias SceneStorage = TokamakCore.SceneStorage
|
|||
|
||||
// MARK: Misc
|
||||
|
||||
public typealias ViewBuilder = TokamakCore.ViewBuilder
|
||||
|
||||
// FIXME: I would put this inside TokamakCore, but for
|
||||
// some reason it doesn't get exported with the typealias
|
||||
extension Text {
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
//
|
||||
// Created by Carson Katri on 6/29/20.
|
||||
//
|
||||
|
||||
import TokamakCore
|
||||
|
||||
extension Path: ViewDeferredToRenderer {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2020 Tokamak contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import TokamakCore
|
||||
|
||||
extension NavigationLink: ViewDeferredToRenderer {
|
||||
public var deferredBody: AnyView {
|
||||
let proxy = _NavigationLinkProxy(self)
|
||||
return AnyView(
|
||||
HTML("a", [
|
||||
"href": "javascript:void%200",
|
||||
], listeners: [
|
||||
// FIXME: Focus destination or something so assistive
|
||||
// technology knows where to look when clicking the link.
|
||||
"click": { _ in proxy.activate() },
|
||||
]) { proxy.label }
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2020 Tokamak contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import JavaScriptKit
|
||||
import TokamakCore
|
||||
|
||||
extension NavigationView: ViewDeferredToRenderer {
|
||||
public var deferredBody: AnyView {
|
||||
AnyView(HTML("div", [
|
||||
"style": """
|
||||
display: flex; flex-direction: row; align-items: stretch;
|
||||
width: 100%; height: 100%;
|
||||
""",
|
||||
]) {
|
||||
_NavigationViewProxy(self).body
|
||||
})
|
||||
}
|
||||
}
|
|
@ -59,6 +59,7 @@ public struct ColorDemo: View {
|
|||
@State private var v2: String = "0.5"
|
||||
|
||||
public var body: some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
Button("Input \(colorForm.rawValue.uppercased())") {
|
||||
colorForm = colorForm == .rgb ? .hsb : .rgb
|
||||
|
@ -81,6 +82,7 @@ public struct ColorDemo: View {
|
|||
.padding()
|
||||
.background($0)
|
||||
}
|
||||
}.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ struct Counter: View {
|
|||
|
||||
let limit: Int
|
||||
|
||||
public var body: some View {
|
||||
@ViewBuilder public var body: some View {
|
||||
if count.value < limit {
|
||||
VStack {
|
||||
Button("Increment") { count.value += 1 }
|
||||
|
|
|
@ -23,7 +23,7 @@ public struct ForEachDemo: View {
|
|||
Text("Add item")
|
||||
}
|
||||
|
||||
ForEach(0..<maxItem) {
|
||||
ForEach(0..<maxItem, id: \.self) {
|
||||
Text("Item: \($0)")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ struct File: Identifiable {
|
|||
let children: [File]?
|
||||
}
|
||||
|
||||
@available(OSX 10.16, iOS 14, *)
|
||||
@available(OSX 10.16, iOS 14.0, *)
|
||||
struct OutlineGroupDemo: View {
|
||||
let fs: [File] = [
|
||||
.init(id: 0, name: "Users", children: [
|
||||
|
|
|
@ -19,10 +19,14 @@ import TokamakShim
|
|||
|
||||
struct SpacerDemo: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Text("Left side.")
|
||||
Spacer()
|
||||
Text("Right side.")
|
||||
}
|
||||
Spacer()
|
||||
Text("Forced to bottom.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,56 +17,132 @@
|
|||
|
||||
import TokamakShim
|
||||
|
||||
struct TokamakDemoView: View {
|
||||
var body: some View {
|
||||
ScrollView(showsIndicators: true) {
|
||||
HStack {
|
||||
Spacer()
|
||||
func title<V>(_ view: V, title: String) -> AnyView where V: View {
|
||||
if #available(OSX 10.16, iOS 14.0, *) {
|
||||
return AnyView(view.navigationTitle(title))
|
||||
} else {
|
||||
#if !os(macOS)
|
||||
return AnyView(view.navigationBarTitle(title))
|
||||
#else
|
||||
return AnyView(view)
|
||||
#endif
|
||||
}
|
||||
VStack {
|
||||
Group {
|
||||
}
|
||||
|
||||
struct NavItem: Identifiable {
|
||||
var id: String
|
||||
var destination: AnyView?
|
||||
|
||||
init<V>(_ id: String, destination: V) where V: View {
|
||||
self.id = id
|
||||
self.destination = title(destination.frame(minWidth: 300), title: id)
|
||||
}
|
||||
|
||||
init(unavailiable id: String) {
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
var outlineGroupDemo: NavItem {
|
||||
if #available(OSX 10.16, iOS 14.0, *) {
|
||||
return NavItem("OutlineGroup", destination: OutlineGroupDemo())
|
||||
} else {
|
||||
return NavItem(unavailiable: "OutlineGroup")
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(macOS)
|
||||
var listDemo: AnyView {
|
||||
if #available(iOS 14.0, *) {
|
||||
return AnyView(ListDemo().listStyle(InsetGroupedListStyle()))
|
||||
} else {
|
||||
return AnyView(ListDemo())
|
||||
}
|
||||
}
|
||||
#else
|
||||
var listDemo = ListDemo()
|
||||
#endif
|
||||
|
||||
var gridDemo: NavItem {
|
||||
if #available(OSX 10.16, iOS 14.0, *) {
|
||||
return NavItem("Grid", destination: GridDemo())
|
||||
} else {
|
||||
return NavItem(unavailiable: "Grid")
|
||||
}
|
||||
}
|
||||
|
||||
var appStorageDemo: NavItem {
|
||||
if #available(OSX 11.0, iOS 14.0, *) {
|
||||
return NavItem("AppStorage", destination: AppStorageDemo())
|
||||
} else {
|
||||
return NavItem(unavailiable: "AppStorage")
|
||||
}
|
||||
}
|
||||
|
||||
var links: [NavItem] {
|
||||
[
|
||||
NavItem("Counter", destination:
|
||||
Counter(count: Count(value: 5), limit: 15)
|
||||
.padding()
|
||||
.background(Color(red: 0.9, green: 0.9, blue: 0.9, opacity: 1.0))
|
||||
.border(Color.red, width: 3)
|
||||
ZStack {
|
||||
.border(Color.red, width: 3)),
|
||||
NavItem("ZStack", destination: ZStack {
|
||||
Text("I'm on bottom")
|
||||
Text("I'm forced to the top")
|
||||
.zIndex(1)
|
||||
Text("I'm on top")
|
||||
}.padding(20)),
|
||||
NavItem("ForEach", destination: ForEachDemo()),
|
||||
NavItem("Text", destination: TextDemo()),
|
||||
NavItem("Toggle", destination: ToggleDemo()),
|
||||
NavItem("Path", destination: PathDemo()),
|
||||
NavItem("TextField", destination: TextFieldDemo()),
|
||||
NavItem("Spacer", destination: SpacerDemo()),
|
||||
NavItem("Environment", destination: EnvironmentDemo().font(.system(size: 8))),
|
||||
NavItem("Picker", destination: PickerDemo()),
|
||||
NavItem("List", destination: listDemo),
|
||||
outlineGroupDemo,
|
||||
NavItem("Color", destination: ColorDemo()),
|
||||
appStorageDemo,
|
||||
gridDemo,
|
||||
]
|
||||
}
|
||||
.padding(20)
|
||||
}
|
||||
Group {
|
||||
if #available(OSX 11, iOS 14, *) {
|
||||
AppStorageDemo()
|
||||
}
|
||||
ForEachDemo()
|
||||
TextDemo()
|
||||
ToggleDemo()
|
||||
PathDemo()
|
||||
TextFieldDemo()
|
||||
SpacerDemo()
|
||||
EnvironmentDemo()
|
||||
.font(.system(size: 8))
|
||||
PickerDemo()
|
||||
}
|
||||
Group {
|
||||
#if canImport(TokamakDOM)
|
||||
ListDemo().listStyle(InsetGroupedListStyle())
|
||||
|
||||
struct TokamakDemoView: View {
|
||||
var body: some View {
|
||||
NavigationView { () -> AnyView in
|
||||
let list = title(
|
||||
List(links) { link in
|
||||
if let dest = link.destination {
|
||||
NavigationLink(link.id, destination: HStack {
|
||||
Spacer()
|
||||
dest
|
||||
Spacer()
|
||||
})
|
||||
} else {
|
||||
#if os(WASI)
|
||||
Text(link.id)
|
||||
#else
|
||||
ListDemo()
|
||||
HStack {
|
||||
Text(link.id)
|
||||
Spacer()
|
||||
Text("unavailable").opacity(0.5)
|
||||
}
|
||||
#endif
|
||||
if #available(OSX 10.16, iOS 14.0, *) {
|
||||
OutlineGroupDemo()
|
||||
}
|
||||
ColorDemo()
|
||||
.padding()
|
||||
if #available(OSX 10.16, iOS 14.0, *) {
|
||||
GridDemo()
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 300),
|
||||
title: "Demos"
|
||||
)
|
||||
#if os(WASI)
|
||||
return AnyView(list)
|
||||
#else
|
||||
if #available(iOS 14.0, *) {
|
||||
return AnyView(list.listStyle(SidebarListStyle()))
|
||||
} else {
|
||||
return AnyView(list)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.environmentObject(TestEnvironment())
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ Table columns:
|
|||
| | | |
|
||||
| --- | ------------------------------------------------------------------------------------------------ | :-: |
|
||||
| 🚧 | [Button](https://developer.apple.com/documentation/swiftui/button) | |
|
||||
| | [NavigationLink](https://developer.apple.com/documentation/swiftui/navigationlink) | |
|
||||
| 🚧 | [NavigationLink](https://developer.apple.com/documentation/swiftui/navigationlink) | |
|
||||
| | [EditButton](https://developer.apple.com/documentation/swiftui/editbutton) | |
|
||||
| | [PasteButton](https://developer.apple.com/documentation/swiftui/pastebutton) | |
|
||||
| | [SignInWithAppleButton](https://developer.apple.com/documentation/swiftui/signinwithapplebutton) | β |
|
||||
|
@ -117,7 +117,7 @@ Table columns:
|
|||
|
||||
| | | |
|
||||
| --- | ---------------------------------------------------------------------------------- | :-: |
|
||||
| | [NavigationView](https://developer.apple.com/documentation/swiftui/navigationview) | |
|
||||
| 🚧 | [NavigationView](https://developer.apple.com/documentation/swiftui/navigationview) | |
|
||||
| | [TabView](https://developer.apple.com/documentation/swiftui/tabview) | |
|
||||
| | [HSplitView](https://developer.apple.com/documentation/swiftui/hsplitview) | |
|
||||
| | [VSplitView](https://developer.apple.com/documentation/swiftui/vsplitview) | |
|
||||
|
|
Loading…
Reference in New Issue