そのため、SwiftUIでリストを作成すると、「無料」のマスター詳細分割ビューが表示されます。
だから例えばこれで:
import SwiftUI
struct ContentView : View {
var people = ["Angela", "Juan", "Yeji"]
var body: some View {
NavigationView {
List {
ForEach(people, id: \.self) { person in
NavigationLink(destination: Text("Hello!")) {
Text(person)
}
}
}
Text("????")
}
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
IPadシミュレーターが横向きで、最初の詳細画面が絵文字の場合、splitViewが表示されます。しかし、名前をタップすると、詳細ビューは「Hello!」になります。
すばらしいことです。
しかし、iPadを縦向きで実行すると、ユーザーは絵文字で挨拶され、リストがあることを示すものはありません。リストを横から表示するには、左から右にスワイプする必要があります。
ユーザーがタップして左側のアイテムのリストを表示できるようにするナビゲーションバーを表示する方法を知っている人はいますか?絵文字だけの画面にならないように?
「左からスワイプしてファイル/ユーザー/その他のリストを表示する」というメモを残したくない
UISplitViewControllerには、設定可能な折りたたまれたプロパティがあったことを覚えています。ここにそのようなものはありますか?
Xcode 11 beta 3では、Appleが.navigationViewStyle(style:)
をNavigationView
に追加しました。
Xcode 11 Beta 5向けに更新されました。
createMasterView()&DetailsView().
struct MyMasterView: View {
var people = ["Angela", "Juan", "Yeji"]
var body: some View {
List {
ForEach(people, id: \.self) { person in
NavigationLink(destination: DetailsView()) {
Text(person)
}
}
}
}
}
struct DetailsView: View {
var body: some View {
Text("Hello world")
.font(.largeTitle)
}
}
私の内部ContentView:
var body: some View {
NavigationView {
MyMasterView()
DetailsView()
}.navigationViewStyle(DoubleColumnNavigationViewStyle())
.padding()
}
出力:
現時点では、Xcode 11.2.1では何も変更されていません。 iPadのSplitViewでも同じ問題があり、Ketan Odedra応答のようにパディングを追加することで解決しましたが、少し修正しました。
var body: some View {
GeometryReader { geometry in
NavigationView {
MasterView()
DetailsView()
}
.navigationViewStyle(DoubleColumnNavigationViewStyle())
.padding(.leading, leadingPadding(geometry))
}
}
private func leadingPadding(_ geometry: GeometryProxy) -> CGFloat {
if UIDevice.current.userInterfaceIdiom == .pad {
return 0.5
}
return 0
}
これはシミュレータで完全に機能します。しかし、レビューのためにアプリを送信すると、拒否されました。この小さなハックは、レビュアーデバイスでは機能しません。私は本物のiPadを持っていないので、何が原因なのかわかりません。それを試してください、多分それはあなたのために働くでしょう。
それは私にとってはうまくいきませんが、Apple DTSに助けを求めました。今のところ、SwiftUI APIはUIKitのSplitViewControllerの動作を完全にシミュレートできないとのことです。しかし、回避策:SwiftUIでカスタムSplitViewを作成できます。
struct SplitView<Master: View, Detail: View>: View {
var master: Master
var detail: Detail
init(@ViewBuilder master: () -> Master, @ViewBuilder detail: () -> Detail) {
self.master = master()
self.detail = detail()
}
var body: some View {
let viewControllers = [UIHostingController(rootView: master), UIHostingController(rootView: detail)]
return SplitViewController(viewControllers: viewControllers)
}
}
struct SplitViewController: UIViewControllerRepresentable {
var viewControllers: [UIViewController]
@Environment(\.splitViewPreferredDisplayMode) var preferredDisplayMode: UISplitViewController.DisplayMode
func makeUIViewController(context: Context) -> UISplitViewController {
return UISplitViewController()
}
func updateUIViewController(_ splitController: UISplitViewController, context: Context) {
splitController.preferredDisplayMode = preferredDisplayMode
splitController.viewControllers = viewControllers
}
}
struct PreferredDisplayModeKey : EnvironmentKey {
static var defaultValue: UISplitViewController.DisplayMode = .automatic
}
extension EnvironmentValues {
var splitViewPreferredDisplayMode: UISplitViewController.DisplayMode {
get { self[PreferredDisplayModeKey.self] }
set { self[PreferredDisplayModeKey.self] = newValue }
}
}
extension View {
/// Sets the preferred display mode for SplitView within the environment of self.
func splitViewPreferredDisplayMode(_ mode: UISplitViewController.DisplayMode) -> some View {
self.environment(\.splitViewPreferredDisplayMode, mode)
}
}
そしてそれを使います:
SplitView(master: {
MasterView()
}, detail: {
DetailView()
}).splitViewPreferredDisplayMode(.allVisible)
IPadでは動作します。しかし、1つの問題があります(多分もっと..)。 MasterViewとDetailViewの両方にNavigationViewがあるため、このアプローチはiPhoneのナビゲーションを台無しにします。
UPDATE:最後に、Xcode 11.4 Beta 2では、非表示のマスタービューを示すボタンをナビゲーションバーに追加しました。
シミュレータでのテストは最小限ですが、これは実際のソリューションに近いはずです。アイデアは、EnvironmentObject
を使用して、二重列NavigationStyle
を使用するか、単一列を使用するかについて公開された変数を保持し、その変数がある場合にNavigationView
を再作成することです変更。
EnvironmentObject:
final class AppEnvironment: ObservableObject {
@Published var useSideBySide: Bool = false
}
シーンデリゲートで、起動時に変数を設定し、デバイスの回転を観察し、場合によっては変更します(「1000」は正しい値ではありません。開始点)。
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var appEnvironment = AppEnvironment()
@objc
func orientationChanged() {
let bounds = UIScreen.main.nativeBounds
let orientation = UIDevice.current.orientation
// 1000 is a starting point, should be smallest height of a + size iPhone
if orientation.isLandscape && bounds.size.height > 1000 {
if appEnvironment.useSideBySide == false {
appEnvironment.useSideBySide = true
print("SIDE changed to TRUE")
}
} else if orientation.isPortrait && appEnvironment.useSideBySide == true {
print("SIDE changed to false")
appEnvironment.useSideBySide = false
}
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(appEnvironment))
self.window = window
window.makeKeyAndVisible()
orientationChanged()
NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil)
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
}
NavigationView
が作成されるトップレベルのコンテンツビューで、navigationViewStyle
を直接使用する代わりに、カスタム修飾子を使用します。
struct ContentView: View {
@State private var dates = [Date]()
var body: some View {
NavigationView {
MV(dates: $dates)
DetailView()
}
.modifier( WTF() )
}
struct WTF: ViewModifier {
@EnvironmentObject var appEnvironment: AppEnvironment
func body(content: Content) -> some View {
Group {
if appEnvironment.useSideBySide == true {
content
.navigationViewStyle(DoubleColumnNavigationViewStyle())
} else {
content
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
}
}
先に述べたように、シミュレーターのテストだけですが、マスター表示で回転、詳細表示で回転の両方の方向で起動してみましたが、どれも見栄えがいいです。
import SwiftUI
var hostingController: UIViewController?
func showList() {
let split = hostingController?.children[0] as? UISplitViewController
UIView.animate(withDuration: 0.3, animations: {
split?.preferredDisplayMode = .primaryOverlay
}) { _ in
split?.preferredDisplayMode = .automatic
}
}
func hideList() {
let split = hostingController?.children[0] as? UISplitViewController
split?.preferredDisplayMode = .primaryHidden
}
// =====
struct Dest: View {
var person: String
var body: some View {
VStack {
Text("Hello! \(person)")
Button(action: showList) {
Image(systemName: "sidebar.left")
}
}
.onAppear(perform: hideList)
}
}
struct ContentView : View {
var people = ["Angela", "Juan", "Yeji"]
var body: some View {
NavigationView {
List {
ForEach(people, id: \.self) { person in
NavigationLink(destination: Dest(person: person)) {
Text(person)
}
}
}
VStack {
Text("????")
Button(action: showList) {
Image(systemName: "sidebar.left")
}
}
}
}
}
import PlaygroundSupport
hostingController = UIHostingController(rootView: ContentView())
PlaygroundPage.current.setLiveView(hostingController!)