簡體   English   中英

SwiftUI 關閉模式

[英]SwiftUI dismiss modal

由於 SwiftUI 是聲明性的,因此沒有dismiss方法。 如何向DetailView添加關閉/關閉按鈕?

struct DetailView: View {
  var body: some View {
  Text("Detail")
  }
}

struct ContentView : View {
  var body: some View {
  PresentationButton(Text("Click to show"), destination: DetailView())
  }
}

使用@State 屬性包裝器(推薦)

struct ContentView: View {
    @State private var showModal = false
    
    var body: some View {
       Button("Show Modal") {
          self.showModal.toggle()
       }.sheet(isPresented: $showModal) {
            ModalView(showModal: self.$showModal)
        }
    }
}

struct ModalView: View {
    @Binding var showModal: Bool
    
    var body: some View {
        Text("Modal view")
        Button("Dismiss") {
            self.showModal.toggle()
        }
    }
}

使用presentationMode

您可以在模態視圖中使用presentationMode環境變量並調用self.presentaionMode.wrappedValue.dismiss()來關閉模態:

struct ContentView: View {

  @State private var showModal = false

  // If you are getting the "can only present once" issue, add this here.
  // Fixes the problem, but not sure why; feel free to edit/explain below.
  @SwiftUI.Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>


  var body: some View {
    Button(action: {
        self.showModal = true
    }) {
        Text("Show modal")
    }.sheet(isPresented: self.$showModal) {
        ModalView()
    }
  }
}


struct ModalView: View {

  @Environment(\.presentationMode) private var presentationMode

  var body: some View {
    Group {
      Text("Modal view")
      Button(action: {
         self.presentationMode.wrappedValue.dismiss()
      }) {
        Text("Dismiss")
      }
    }
  }
}

在此處輸入圖片說明

在 Xcode Beta 5 中,另一種方法是在啟動模態的視圖中使用@State,並在模態視圖中添加一個綁定來控制模態的可見性。 這不需要您進入@EnvironmentpresentationMode 變量。

struct MyView : View {
    @State var modalIsPresented = false

    var body: some View {
        Button(action: {self.modalIsPresented = true})  {
            Text("Launch modal view")
        }
        .sheet(isPresented: $modalIsPresented, content: {
            MyModalView(isPresented: self.$modalIsPresented)
        })
    }
}


struct MyModalView : View {
    @Binding var isPresented: Bool

    var body: some View {
        Button(action: {self.isPresented = false})  {
            Text("Close modal view")
        }
    }
}

這是一種關閉所呈現視圖的方法。

struct DetailView: View {
    @Binding
    var dismissFlag: Bool

    var body: some View {
        Group {
            Text("Detail")
            Button(action: {
                self.dismissFlag.toggle()
            }) {
                Text("Dismiss")
            }
        }

    }
}

struct ContentView : View {
    @State var dismissFlag = false

    var body: some View {
        Button(action: {
            self.dismissFlag.toggle()
        })
        { Text("Show") }
            .presentation(!dismissFlag ? nil :
                Modal(DetailView(dismissFlag: $dismissFlag)) {
                print("dismissed")
            })
    }
}

在此處輸入圖片說明

似乎對於 Xcode 11 Beta 7(這是在 Xcode 的 build 11M392r 上),它略有不同。

@Environment(\.presentationMode) var presentation


Button(action: { self.presentation.wrappedValue.dismiss() }) { Text("Dismiss") }

你可以實現這個。

struct view: View {
    @Environment(\.isPresented) private var isPresented

    private func dismiss() {
        isPresented?.value = false
    }
}

現在在 Beta 5 中有一種非常干凈的方法可以做到這一點。

import SwiftUI

struct ModalView : View {
    // In Xcode 11 beta 5, 'isPresented' is deprecated use 'presentationMode' instead
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        Group {
            Text("Modal view")
            Button(action: { self.presentationMode.wrappedValue.dismiss() }) { Text("Dismiss") }
        }
    }
}

struct ContentView : View {
    @State var showModal: Bool = false
    var body: some View {
        Group {
            Button(action: { self.showModal = true }) { Text("Show modal via .sheet modifier") }
                .sheet(isPresented: $showModal, onDismiss: { print("In DetailView onDismiss.") }) { ModalView() }
        }
    }
}

由於PresentationButton易於使用,但隱藏狀態會破壞SwiftUI的預測特性, SwiftUI我使用可訪問的Binding實現了它。

public struct BindedPresentationButton<Label, Destination>: View where Label: View, Destination: View {
    /// The state of the modal presentation, either `visibile` or `off`.
    private var showModal: Binding<Bool>

    /// A `View` to use as the label of the button.
    public var label: Label

    /// A `View` to present.
    public var destination: Destination

    /// A closure to be invoked when the button is tapped.
    public var onTrigger: (() -> Void)?

    public init(
        showModal: Binding<Bool>,
        label: Label,
        destination: Destination,
        onTrigger: (() -> Void)? = nil
    ) {
        self.showModal = showModal
        self.label = label
        self.destination = destination
        self.onTrigger = onTrigger
    }

    public var body: some View {
        Button(action: toggleModal) {
            label
        }
        .presentation(
            !showModal.value ? nil :
                Modal(
                    destination, onDismiss: {
                        self.toggleModal()
                    }
                )
        )
    }

    private func toggleModal() {
        showModal.value.toggle()
        onTrigger?()
    }
}

這是它的使用方式:

struct DetailView: View {
    @Binding var showModal: Bool

    var body: some View {
        Group {
            Text("Detail")
            Button(action: {
                self.showModal = false
            }) {
                Text("Dismiss")
            }
        }
    }
}

struct ContentView: View {
    @State var showModal = false

    var body: some View {
        BindedPresentationButton(
            showModal: $showModal,
            label: Text("Show"),
            destination: DetailView(showModal: $showModal)
        ) {
            print("dismissed")
        }
    }
}

iOS 15

我們現在可以使用DismissAction代替presentationMode

這是文檔中的一個示例:

struct SheetView: View {
    @Environment(\.dismiss) var dismiss

    var body: some View {
        NavigationView {
            SheetContents()
                .toolbar {
                    Button("Done") {
                        dismiss()
                    }
                }
        }
    }
}

在 Xcode 11.0 beta 7 中,該值現在已包裝,以下功能對我有用:

func dismiss() {
    self.presentationMode.wrappedValue.dismiss()
}

SwiftUI 中的模態視圖似乎很簡單,直到您開始在ListForm視圖中使用它們。 我創建了一個包含所有邊緣情況的小型庫,並使模態視圖的使用與NavigationView - NavigationLink對相同。

該庫在這里是開源的: https : //github.com/diniska/modal-view 您可以使用 Swift Package Manager 將其包含到項目中,或者只需復制庫包含的單個文件。

您的代碼的解決方案是:

struct DetailView: View {
    var dismiss: () -> ()
    var body: some View {
        Text("Detail")
        Button(action: dismiss) {
            Text("Click to dismiss")
        }
    }
}

struct ContentView : View {
    var body: some View {
        ModalPresenter {
            ModalLink(destination: DetailView.init(dismiss:)) {
                Text("Click to show")
            }
        }
    }
}

此外,還有一篇包含完整描述和示例的文章: How to present modal view in SwiftUI

Navigation自動彈出或在Modal關閉


只需從目標視圖中的環境中獲取presentationMode從中dismiss wrappedValue

struct DestinationView: View {
    @Environment(\.presentationMode) private var presentationMode

    var body: some View {
        Button("Dismiss") {
            self.presentationMode.wrappedValue.dismiss()
        }
    }
}


演示(彈出/關閉)

流行音樂 解雇

在 PresentationMode 使用環境變量。 此 GitHub 鏈接可能會幫助您解決問題https://github.com/MannaICT13/Sheet-in-SwiftUI

這是簡單的解決方案:

struct ContentView2 : View {

    @Environment (\.presentationMode) var presentationMode

    var body : some View {
        VStack {
            Text("This is ContentView2")
            Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }, label: {
                Text("Back")    
            })    
        }
    }
}


struct ContentView: View {

    @State var isShowingSheet : Bool = false

    var body: some View {
        Button(action: {
            self.isShowingSheet.toggle()
        }, label: {
            Text("Click Here")
        }).sheet(isPresented: $isShowingSheet, content: {  
            ContentView2()
        })
    }
}

執行此操作的一種方法可能是為模態呈現和解除聲明您自己的修飾符。

extension View {

  func showModal<T>(_ binding: Binding<Bool>, _ view: @escaping () -> T) -> some View where T: View {

    let windowHeightOffset = (UIApplication.shared.windows.first?.frame.height ?? 600) * -1

    return ZStack {

      self

      view().frame(maxWidth: .infinity, maxHeight: .infinity).edgesIgnoringSafeArea(.all).offset(x: 0, y: binding.wrappedValue ? 0 : windowHeightOffset)

    }

  }
}

然后您可以在任何您希望告訴如何顯示視圖和關閉該視圖的視圖上使用修飾符。 就像彈出框或工作表修改器一樣。

struct ContentView: View {

  @State var showModal = false

  var body: some View {

    Text("Show").foregroundColor(.blue).onTapGesture {
      withAnimation(.easeIn(duration: 0.75)) {
        self.showModal = true
      }
    }.showModal($showModal, {

      Text("Dismiss").foregroundColor(.blue).onTapGesture {
        withAnimation(.easeIn(duration: 0.75)) {
          self.showModal = false
        }
      }

    })


  }
}    

演示文稿從頂部全屏顯示,如果您希望它來自側面,請將修改器內部的過渡更改為前導或尾隨。 其他過渡也可以工作,例如不透明度或比例。

在此處輸入圖片說明

SwiftUI 2 代碼示例(也適用於手機)

(示例代碼@main用於 swift 1,但您仍然可以在沒有@main塊的情況下嘗試)

使用工作表的完整應用程序示例:

@main
struct TestAppApp: App {
    var body: some Scene {
        WindowGroup {
            SheetLink(text: "click me!", content: ChildView() )
                .padding(.all, 100)
        }
    }
}

struct ChildView: View {
    var body: some View {
        Text("this is subView!")
    }
}

在此處輸入圖片說明

當子視圖大於主視圖時:

在此處輸入圖片說明

以及這背后的代碼:

struct SheetLink<Content> : View where Content: View {
    @State var text: String
    @State var displaySheet = false
    @State var content: Content


    var body: some View {
        HStack {
            Button(text, action: { self.displaySheet = true } ).buttonStyle(PlainButtonStyle()).foregroundColor(.blue)
        }
        .sheet(isPresented: $displaySheet) {
            SheetTemplateView(isPresented: self.$displaySheet, content: content)
        }
    }
}

struct SheetTemplateView<Content> : View where Content: View {
    @Binding var isPresented: Bool
    @State var content: Content
    
    var body: some View {
        VStack{
            HStack{
                Button("Back!", action: { isPresented.toggle() } ).buttonStyle(PlainButtonStyle()).foregroundColor(.blue)
                Spacer()
            }
            Spacer()
            content
            Spacer()
        }
        .padding()
    }
}

您可以使用演示模式關閉。 宣布

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

然后在需要時,通過

self.presentationMode.wrappedValue.dismiss()

Swift 5.5 和 SwiftUI 3 中的新功能:

@Environment(\.dismiss) var dismiss

然后在函數或正文代碼中的某處,只需調用:

self.dismiss()

您可以使用SheetKit關閉所有工作表

SheetKit().dismissAllSheets()

或呈現新的 UISheetPresentationController

sheetKit.present(with: .bottomSheet){
  Text("Hello world")
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM