programing

여러 시트(isPresented:)가 Swift에서 작동하지 않음UI

telecom 2023. 4. 15. 08:22
반응형

여러 시트(isPresented:)가 Swift에서 작동하지 않음UI

2개의 모드를 하고 있습니다.sheet(isPresented:)두 분 다요. 마지막 한 분만 발표되는 것 같아서요.어떻게 하면 이 문제를 해결할 수 있을까요?또는 Swift에서는 한 뷰에 여러 시트를 사용할 수 없는가? UI?

struct ContentView: View {
    
    @State private var firstIsPresented = false
    @State private var secondIsPresented = false
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Button("First modal view") {
                    self.firstIsPresented.toggle()
                }
                Button ("Second modal view") {
                    self.secondIsPresented.toggle()
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
            .sheet(isPresented: $firstIsPresented) {
                    Text("First modal view")
            }
            .sheet(isPresented: $secondIsPresented) {
                    Text("Only the second modal view works!")
            }
        }
    }
}

위의 코드는 경고 없이 컴파일됩니다(X코드 11.2.1).

업데이트

Xcode 12.5.0 Beta 3 ( 2021년 3월 3일)부터 이 질문은 더 이상 의미가 없습니다.복수가 가능하기 때문입니다..sheet(isPresented:) ★★★★★★★★★★★★★★★★★」.fullScreenCover(isPresented:)연속해서 질문에 제시된 코드가 제대로 작동합니다.

그럼에도 불구하고 이 답변은 시트를 매우 잘 정리하고 코드를 더 깨끗하고 읽기 쉽게 만들기 때문에 여전히 유효하다는 것을 알게 되었습니다.-여러 개의 독립된 불란 대신 하나의 진실 소스를 가지고 있습니다.

실제 답은

iOS 14에서도 사용할 수 있는 최선의 방법:

enum ActiveSheet: Identifiable {
    case first, second
    
    var id: Int {
        hashValue
    }
}

struct YourView: View {
    @State var activeSheet: ActiveSheet?

    var body: some View {
        VStack {
            Button {
                activeSheet = .first
            } label: {
                Text("Activate first sheet")
            }

            Button {
                activeSheet = .second
            } label: {
                Text("Activate second sheet")
            }
        }
        .sheet(item: $activeSheet) { item in
            switch item {
            case .first:
                FirstView()
            case .second:
                SecondView()
            }
        }
    }
}

자세한 내용은 이쪽:https://developer.apple.com/documentation/swiftui/view/sheet(item:ondismiss:content:)

, 「 」를 설정해 주세요.activeSheet = nil

보너스: 시트를 전체 화면으로 만들려면 동일한 코드를 사용합니다..sheet를 쓰다.fullScreenCover

아래 코드를 입력해 주세요.

응답 업데이트(iOS 14, Xcode 12)

enum ActiveSheet {
   case first, second
   var id: Int {
      hashValue
   }
}

struct ContentView: View {

    @State private var showSheet = false
    @State private var activeSheet: ActiveSheet? = .first

    var body: some View {
    
        NavigationView {
            VStack(spacing: 20) {
                Button("First modal view") {
                    self.showSheet = true
                    self.activeSheet = .first
                }
                Button ("Second modal view") {
                    self.showSheet = true
                    self.activeSheet = .second
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
            .sheet(isPresented: $showSheet) {
                if self.activeSheet == .first {
                    Text("First modal view")
                }
                else {
                    Text("Only the second modal view works!")
                }
            }
        }
    }
}

뷰의 배경에 배치된 EmptyView에 시트를 추가할 수도 있습니다.이 작업은 여러 번 수행할 수 있습니다.

  .background(EmptyView()
        .sheet(isPresented: isPresented, content: content))

고객의 케이스는 다음 방법으로 해결할 수 있습니다(Xcode 11.2로 테스트 완료).

var body: some View {

    NavigationView {
        VStack(spacing: 20) {
            Button("First modal view") {
                self.firstIsPresented.toggle()
            }
            .sheet(isPresented: $firstIsPresented) {
                    Text("First modal view")
            }
            Button ("Second modal view") {
                self.secondIsPresented.toggle()
            }
            .sheet(isPresented: $secondIsPresented) {
                    Text("Only the second modal view works!")
            }
        }
        .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
    }
}

버튼과 .sheet 호출을 그룹화하는 것만으로 이 작업을 수행할 수 있습니다.선두와 후행의 각각이 있는 경우는, 그 정도로 간단합니다.단, 선두 또는 후행 중 하나에 여러 개의 네비게이션바리템이 있는 경우 HStack으로 랩하고 각 버튼을 시트콜과 함께 VStack으로 랩해야 합니다.

다음은 두 개의 후행 버튼의 예입니다.

            trailing:
            HStack {
                VStack {
                    Button(
                        action: {
                            self.showOne.toggle()
                    }
                    ) {
                        Image(systemName: "camera")
                    }
                    .sheet(isPresented: self.$showOne) {
                        OneView().environment(\.managedObjectContext, self.managedObjectContext)
                    }
                }//showOne vstack

                VStack {
                    Button(
                        action: {
                            self.showTwo.toggle()
                    }
                    ) {
                        Image(systemName: "film")
                    }
                    .sheet(isPresented: self.$showTwo) {
                        TwoView().environment(\.managedObjectContext, self.managedObjectContext)
                    }
                }//show two vstack
            }//nav bar button hstack

커스텀 버튼뷰와 콜시트를 작성하면 이 문제가 해결됩니다.

struct SheetButton<Content>: View where Content : View {

    var text: String
    var content: Content
    @State var isPresented = false

    init(_ text: String, @ViewBuilder content: () -> Content) {
        self.text = text
        self.content = content()
    }

    var body: some View {
        Button(text) {
            self.isPresented.toggle()
        }
        .sheet(isPresented: $isPresented) {
            self.content
        }
    }
}

Content View가 더 깨끗해집니다.

struct ContentView: View {

    var body: some View {

        NavigationView {
            VStack(spacing: 20) {
                SheetButton("First modal view") {
                    Text("First modal view")
                }
                SheetButton ("Second modal view") {
                    Text("Only the second modal view works!")
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
        }
    }
}

이 방법은 List 행 내용에 따라 시트를 여는 경우에도 정상적으로 작동합니다.

struct ContentView: View {

    var body: some View {

        NavigationView {
            List(1...10, id: \.self) { row in
                SheetButton("\(row) Row") {
                    Text("\(row) modal view")
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
        }
    }
}

iOS 및 iPad의 경우OS 14.5 베타 3 및 공개될 때마다 예상대로 여러 시트가 작동하며 다른 답변의 회피책은 필요하지 않습니다.릴리스 노트:

SwiftUI

iOS 및 iPad로 해결OS 14.5 베타 3

해서 여러 개의 기한을 적용할 수 되었습니다.sheet(isPresented:onDismiss:content:) ★★★★★★★★★★★★★★★★★」fullScreenCover(item:onDismiss:content:))

Rohit Makwana의 답변과 더불어, 저는 컴파일러가 제 거인의 활자를 확인하는 데 어려움을 겪고 있었기 때문에 시트의 내용을 함수로 추출하는 방법을 찾았습니다.View.

extension YourView {
    enum Sheet {
        case a, b
    }

    @ViewBuilder func sheetContent() -> some View {
        if activeSheet == .a {
            A()
        } else if activeSheet == .b {
            B()
        }
    }
}

다음과 같이 사용할 수 있습니다.

.sheet(isPresented: $isSheetPresented, content: sheetContent)

코드를 깔끔하게 하고 컴파일러의 스트레스도 해소합니다.

이 질문에는 이미 많은 답이 있다는 것을 알지만, 저는 이 문제에 대해 매우 유용한 다른 가능한 해결책을 찾았습니다.이런 문구가 있으면 안에 포장하는 거예요.액션 시트의 경우, iPad의 스크롤 뷰에 있는 다른 솔루션(그룹 내에서 각 시트와 버튼을 감는 것 등)을 사용하면 액션 시트가 이상한 곳으로 이동하는 경우가 많기 때문에, 이 답변으로 iPad의 스크롤 뷰에 있는 액션 시트의 문제를 해결할 수 있습니다.

struct ContentView: View{
    @State var sheet1 = false
    @State var sheet2 = false
    var body: some View{
        VStack{
            Button(action: {
                self.sheet1.toggle()
            },label: {
                Text("Sheet 1")
            }).padding()
            Button(action: {
                self.sheet2.toggle()
            },label: {
                Text("Sheet 2")
            }).padding()
        }
        if self.sheet1{
            Text("")
                .sheet(isPresented: self.$sheet1, content: {
                    Text("Some content here presenting sheet 1")
                })
        }
        if self.sheet2{
            Text("")
                .sheet(isPresented: self.$sheet2, content: {
                    Text("Some content here presenting sheet 2")
                })
        }

    }
}

이 솔루션은 iOS 14.0에서 작동합니다.

이 솔루션은 .sheet(항목:, 내용:) 구성을 사용하고 있습니다.

struct ContentView: View {
    enum ActiveSheet: Identifiable {
        case First, Second
        
        var id: ActiveSheet { self }
    }
    
    @State private var activeSheet: ActiveSheet?

    var body: some View {

        NavigationView {
            VStack(spacing: 20) {
                Button("First modal view") {
                    activeSheet = .First
                }
                Button ("Second modal view") {
                    activeSheet = .Second
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
            .sheet(item: $activeSheet) { sheet in
                switch sheet {
                case .First:
                    Text("First modal view")
                case .Second:
                    Text("Only the second modal view works!")
                }
            }
        }
    }
}

및 4장, 1장 이상의 경보의 를 나타냅니다. 시트 ★★★★★★★★★★★★★★」ContentView13에서는 , 에서는 OK, 13에서는 , iOS 14에서는 OK, OK

에서 : (으) 목적입니다.sheet(item:onDismiss:content:)을 ★★★★★★★★★★★★★★★★★★★로@State및 var에 된 값. 「는 모두 「비즈니스」에.ContentView이와 같이 시트 수나 경고 수에 제한이 없습니다.

아래 코드의 출력을 다음에 나타냅니다.

올인원

import SwiftUI

// exemple which show use of 4 sheets, 
// 1 (or more) alerts, 
// and an actionSheet in the same ContentView
// OK in iOS 13, iOS 14
// OK in Preview

// Any number of sheets, displayed as Views
// can be used for sheets in other views (with unique case values, of course)
enum SheetState {
    case none
    case AddItem
    case PickPhoto
    case DocPicker
    case ActivityController
}

// Make Identifiable
extension SheetState: Identifiable {
    var id: SheetState { self }
}

// the same for Alerts (who are not View, but Alert)
enum AlertState {
    case none
    case Delete
}

extension AlertState: Identifiable {
    var id: AlertState { self }
}

struct ContentView: View {

// Initialized with nil value
@State private var sheetState: SheetState?
@State private var alertState: AlertState?

var body: some View {
    NavigationView {
        Form {
            Text("Hello, world!")
            Section(header: Text("sheets")) {
                addItemButton
                pickDocumentButton
                pickPhoto
                buttonExportView
            }
            Section(header: Text("alert")) {
                confirmDeleteButton
            }
            Section(header: Text("Action sheet")) {
                showActionSheetButton
            }
        }
        .navigationTitle("Sheets & Alerts")
                    
        // ONLY ONE call .sheet(item: ... with required value in enum
        // if item become not nil => display sheet
        // when dismiss sheet (drag the modal view, or use presentationMode.wrappedValue.dismiss in Buttons) => item = nil
        // in other way : if you set item to nil => dismiss sheet
                    
        // in closure, look for which item value display which view
        // the "item" returned value contains the value passed in .sheet(item: ...
        .sheet(item: self.$sheetState) { item in
            if item == SheetState.AddItem {
                addItemView // SwiftUI view
            } else if item == SheetState.DocPicker {
                documentPickerView // UIViewControllerRepresentable
            } else if item == SheetState.PickPhoto {
                imagePickerView // UIViewControllerRepresentable
            } else if item == SheetState.ActivityController {
                activityControllerView // UIViewControllerRepresentable
            }
            
        }
        
        .alert(item: self.$alertState) { item in
            if item == AlertState.Delete {
                return deleteAlert
            } else {
                // Not used, but seem to be required
                // .alert(item: ... MUST return an Alert
                return noneAlert
            }
        }
    }
}

// For cleaner contents : controls, alerts and sheet views are "stocked" in private var

// MARK: - Sheet Views

private var addItemView: some View {
    Text("Add item").font(.largeTitle).foregroundColor(.blue)
    // drag the modal view set self.sheetState to nil
}

private var documentPickerView: some View {
    DocumentPicker() { url in
        if url != nil {
            DispatchQueue.main.async {
                print("url")
            }
        }
        self.sheetState = nil
        // make the documentPicker view dismissed
    }
}

private var imagePickerView: some View {
    ImagePicker() { image in
        if image != nil {
            DispatchQueue.main.async {
                self.logo = Image(uiImage: image!)
            }
        }
        self.sheetState = nil
    }
}

private var activityControllerView: some View {
    ActivityViewController(activityItems: ["Message to export"], applicationActivities: [], excludedActivityTypes: [])
}

// MARK: - Alert Views

private var deleteAlert: Alert {
    Alert(title: Text("Delete?"),
          message: Text("That cant be undone."),
          primaryButton: .destructive(Text("Delete"), action: { print("delete!") }),
          secondaryButton: .cancel())
}

private var noneAlert: Alert {
    Alert(title: Text("None ?"),
          message: Text("No action."),
          primaryButton: .destructive(Text("OK"), action: { print("none!") }),
          secondaryButton: .cancel())
}

// In buttons, action set value in item for .sheet(item: ...
// Set self.sheetState value make sheet displayed
// MARK: - Buttons

private var addItemButton: some View {
    Button(action: { self.sheetState = SheetState.AddItem }) {
        HStack {
            Image(systemName: "plus")
            Text("Add an Item")
        }
    }
}

private var pickDocumentButton: some View {
    Button(action: { self.sheetState = SheetState.DocPicker }) {
        HStack {
            Image(systemName: "doc")
            Text("Choose Document")
        }
    }
}

@State private var logo: Image = Image(systemName: "photo")
private var pickPhoto: some View {
    ZStack {
        HStack {
            Text("Pick Photo ->")
            Spacer()
        }
        HStack {
            Spacer()
            logo.resizable().scaledToFit().frame(height: 36.0)
            Spacer()
        }
    }
    .onTapGesture { self.sheetState = SheetState.PickPhoto }
}

private var buttonExportView: some View {
    Button(action: { self.sheetState = SheetState.ActivityController }) {
        HStack {
            Image(systemName: "square.and.arrow.up").imageScale(.large)
            Text("Export")
        }
    }
}

private var confirmDeleteButton: some View {
    Button(action: { self.alertState = AlertState.Delete}) {
        HStack {
            Image(systemName: "trash")
            Text("Delete!")
        }.foregroundColor(.red)
    }
}

@State private var showingActionSheet = false
@State private var foregroundColor = Color.blue
private var showActionSheetButton: some View {
    Button(action: { self.showingActionSheet = true }) {
        HStack {
            Image(systemName: "line.horizontal.3")
            Text("Show Action Sheet")
        }.foregroundColor(foregroundColor)
    }
    .actionSheet(isPresented: $showingActionSheet) {
        ActionSheet(title: Text("Change foreground"), message: Text("Select a new color"), buttons: [
            .default(Text("Red")) { self.foregroundColor = .red },
            .default(Text("Green")) { self.foregroundColor = .green },
            .default(Text("Blue")) { self.foregroundColor = .blue },
            .cancel()
        ])
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

이것은 iOS 13.x에서 3장의 시트를 표시할 수 있는 앱에서 잘 작동했습니다.재미있는 행동은 iOS 14에서 시작되었다.앱 부팅 시 어떤 이유에서인지 표시할 시트를 선택하면 상태 변수가 설정되지 않고 빈 화면으로 나타납니다.첫 번째 선택지를 계속 선택하면 공백 시트가 계속 표시됩니다.두 번째 선택지(첫 번째 선택지와 다름)를 선택하면 변수가 설정되고 적절한 시트가 표시됩니다.어느 시트를 먼저 선택해도 같은 바하비어가 발생합니다.

벌레요? 아니면 제가 뭘 놓치고 있는 건가요?제 코드는 3개의 시트 옵션을 제외하고 위와 거의 동일하며, 버튼을 눌렀을 때 실행되는 () -> Void라는 인수를 사용하는 커스텀 버튼이 있습니다.iOS 13.x에서는 정상적으로 동작하지만 iOS 14에서는 동작하지 않습니다.

데이브

Edit 2: 최신 iOS 16.4에서 시트, confirmationDialog, popover all이 서로 충돌하는 주요 버그가 있습니다.예를 들어, 팝오버가 표시되어 있고 시트를 표시하려고 하면 시트가 파손되어 다시 표시할 수 없습니다.

편집: iOS 14.5 베타 3에서 이 문제는 해결되었습니다.

SwiftUI iOS 및 iPad로 해결OS 14.5 베타 3

  • 해서 여러 개의 기한을 적용할 수 되었습니다.sheet(isPresented:onDismiss:content:) ★★★★★★★★★★★★★★★★★」fullScreenCover(item:onDismiss:content:))

수정 전에는 각 버튼에 시트 수식자를 적용하는 것이 회피책이었습니다.

struct ContentView: View {

    @State private var firstIsPresented = false
    @State private var secondIsPresented = false

    var body: some View {

        NavigationView {
            VStack(spacing: 20) {
                Button("First modal view") {
                    self.firstIsPresented.toggle()
                }
                .sheet(isPresented: $firstIsPresented) {
                        Text("First modal view")
                }

                Button ("Second modal view") {
                    self.secondIsPresented.toggle()
                }
                .sheet(isPresented: $secondIsPresented) {
                    Text("Second modal view")
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
        }
    }
}

두 시트 모두 동일한 작업을 수행하므로 중복된 기능을 하위 보기로 추출할 수 있습니다.

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                ShowSheet(title:"First modal view")
                ShowSheet(title:"Second modal view")
            }
            .navigationBarTitle(Text("Multiple modal view no problem!"), displayMode: .inline)
        }
    }
}

struct ShowSheet: View {
    @State private var isPresented = false
    let title: String
    var body: some View {
        Button(title) {
            isPresented.toggle()
        }
        .sheet(isPresented: $isPresented) {
            Text(title)
        }
    }
}

인정된 솔루션은 훌륭하게 동작합니다만, 다른 사람이 같은 문제에 직면했을 경우에 대비해, 추가의 증강을 공유하고 싶다고 생각하고 있습니다.

나의 고민

두 개의 버튼이 하나로 작동하는 문제가 있었습니다.조합하여 2개의 버튼을 했습니다.VStack ★★★★★★★★★★★★★★★★★」HStack을 사용하다 한 사람만을 하고 있었다..sheet총격을 가할 수 있는 방법을 가리지 않고요.

솔루션

여기서의 답은 에게 퍼즐의 잃어버린 조각처럼 작용했다.

중 하나를 .buttonStyle(BorderlessButtonStyle()) ★★★★★★★★★★★★★★★★★」.buttonStyle(PlainButtonStyle())각 버튼에 대해 예상대로 단일 버튼처럼 작동하도록 합니다.

여기에 이것을 추가함으로써 가짜 패스를 범했다면 죄송합니다만, StackOverlflow에 글을 올리는 것은 이번이 처음입니다.

" " " " " " ]sheet(isPresented)는 뷰 수식자입니다.

뷰 또는 다른 뷰 수식자에 적용하여 원래 값의 다른 버전을 생성하는 수식자입니다.

Apple Documentation(애플 문서

즉, 동일한 보기를 두 번 수정합니다.SwfitUI 에서는 순서가 중요하기 때문에 마지막 순서만 표시됩니다.

필요한 것은 수정자를 다른 보기에 적용하거나 필요에 따라 시트를 다르게 표시하는 입니다.

여러 시트를 하나의 뷰에 표시하는 또 다른 간단한 방법은 다음과 같습니다.

각 뷰의 프라이빗 var에는 독자적인 Bool @State 값과 .sheet(isPresented: ...콜)이 있습니다.

구현이 간단하며 모두 한 곳에서 필요합니다.iOS 13, iOS 14, 프리뷰로 OK

import SwiftUI

struct OtherContentView: View {
    var body: some View {
        Form {
            Section {
                button1
            }
            Section {
                button2
            }
            Section {
                button3
            }
            Section {
                button4
            }
        }
    }
    
    @State private var showSheet1 = false
    private var button1: some View {
        Text("Sheet 1")
            .onTapGesture { showSheet1 = true }
            .sheet(isPresented: $showSheet1) { Text("Modal Sheet 1") }
    }
    
    @State private var showSheet2 = false
    private var button2: some View {
        Text("Sheet 2")
            .onTapGesture { showSheet2 = true }
            .sheet(isPresented: $showSheet2) { Text("Modal Sheet 2") }
    }
    
    @State private var showSheet3 = false
    private var button3: some View {
        Text("Sheet 3")
            .onTapGesture { showSheet3 = true }
            .sheet(isPresented: $showSheet3) { Text("Modal Sheet 3") }
    }
    
    @State private var showSheet4 = false
    private var button4: some View {
        Text("Sheet 4")
            .onTapGesture { showSheet4 = true }
            .sheet(isPresented: $showSheet4) { Text("Modal Sheet 4") }
    }
}

struct OtherContentView_Previews: PreviewProvider {
    static var previews: some View {
        OtherContentView()
    }
}

the i 잡 i를 해결했다.@State 관찰 한 것을 을 만들 수 있습니다.SheetContext저를 위해 국가를 관리하고 있습니다.그런 다음 단일 컨텍스트 인스턴스만 필요하며 뷰를 시트로 표시하도록 지시할 수 있습니다.

상세한 것에 대하여는, 다음의 블로그 투고를 참조해 주세요.https://danielsaidi.com/blog/2020/06/06/swiftui-sheets

그건 스위프트가 할 수 있는 방법이 아닌 것 같아요.UI를 통해 모든 종류의 뷰를 표시할 수 있습니다.

이 패러다임은 화면에 일부 콘텐츠를 표시하는 특정 보기를 만드는 방식으로 작동하기 때문에 무언가를 표시해야 하는 슈퍼뷰 본문 내에서 여러 보기를 볼 수 있습니다.그래서 스위프트iOS 14의 UI 2는 이를 수락하지 않으며 개발자는 경우에 따라 수락할 수 있는 슈퍼뷰의 모든 프레젠테이션을 호출해야 하지만 특정 보기가 내용을 표시하면 더 나은 순간이 있습니다.

이를 위한 솔루션을 구현하고 iOS 14.1에서 Xcode 12.1을 사용하는 Swift 5.3에서 테스트했습니다.

struct Presentation<Content>: View where Content: View {
    enum Style {
        case sheet
        case popover
        case fullScreenCover
    }

    @State private var isTrulyPresented: Bool = false
    @State private var willPresent: Bool = false
    @Binding private var isPresented: Bool

    let content: () -> Content
    let dismissHandler: (() -> Void)?
    let style: Style

    init(_ style: Style, _ isPresented: Binding<Bool>, onDismiss: (() -> Void)?, content: @escaping () -> Content) {
        self._isPresented = isPresented
        self.content = content
        self.dismissHandler = onDismiss
        self.style = style
    }

    @ViewBuilder
    var body: some View {
        if !isPresented && !willPresent {
            EmptyView()
        } else {
            switch style {
            case .sheet:
                EmptyView()
                    .sheet(isPresented: $isTrulyPresented, onDismiss: dismissHandler, content: dynamicContent)
            case .popover:
                EmptyView()
                    .popover(isPresented: $isTrulyPresented, content: dynamicContent)
            case .fullScreenCover:
                EmptyView()
                    .fullScreenCover(isPresented: $isTrulyPresented, onDismiss: dismissHandler, content: dynamicContent)
            }
        }
    }
}

extension Presentation {
    var dynamicContent: () -> Content {
        if isPresented && !isTrulyPresented {
            OperationQueue.main.addOperation {
                willPresent = true
                OperationQueue.main.addOperation {
                    isTrulyPresented = true
                }
            }
        } else if isTrulyPresented && !isPresented {
            OperationQueue.main.addOperation {
                isTrulyPresented = false
                OperationQueue.main.addOperation {
                    willPresent = false
                }
            }
        }

        return content
    }
}

그 후 Swift의 모든 뷰에 대해 이러한 메서드를 구현할 수 있습니다.UI

public extension View {
    func _sheet<Content>(
        isPresented: Binding<Bool>,
        content: @escaping () -> Content
    ) -> some View where Content: View {

        self.background(
            Presentation(
                .sheet,
                isPresented,
                onDismiss: nil,
                content: content
            )
        )
    }

    func _sheet<Content>(
        isPresented: Binding<Bool>,
        onDismiss: @escaping () -> Void,
        content: @escaping () -> Content
    ) -> some View where Content: View {

        self.background(
            Presentation(
                .sheet,
                isPresented,
                onDismiss: onDismiss,
                content: content
            )
        )
    }
}

public extension View {
    func _popover<Content>(
        isPresented: Binding<Bool>,
        content: @escaping () -> Content
    ) -> some View where Content: View {

        self.background(
            Presentation(
                .popover,
                isPresented,
                onDismiss: nil,
                content: content
            )
        )
    }
}

public extension View {
    func _fullScreenCover<Content>(
        isPresented: Binding<Bool>,
        content: @escaping () -> Content
    ) -> some View where Content: View {

        self.background(
            Presentation(
                .fullScreenCover,
                isPresented,
                onDismiss: nil,
                content: content
            )
        )
    }

    func _fullScreenCover<Content>(
        isPresented: Binding<Bool>,
        onDismiss: @escaping () -> Void,
        content: @escaping () -> Content
    ) -> some View where Content: View {

        self.background(
            Presentation(
                .fullScreenCover,
                isPresented,
                onDismiss: onDismiss,
                content: content
            )
        )
    }
}

상기의 회답변

  1. 두 시트의 순차적 관계가 있는 경우 이전 시트를 교체할 수 있습니다.
    import SwiftUI
    struct Sheet1: View {
        @Environment(\.dismiss) private var dismiss
        @State var text: String = "Text"
        
        var body: some View {
            
            Text(self.text)
            if self.text == "Modified Text" {
                Button {
                    dismiss()
                } label: {
                    Text("Close sheet")
                }
            } else {
                Button {
                    self.text = "Modified Text"
                } label: {
                    Text("Modify Text")
                }
            }
        }
    }
    struct SheetTester: View {
        @State private var isShowingSheet1 = false
        
        var body: some View {
            Button(action: {
                isShowingSheet1.toggle()
            }) {
                Text("Show Sheet1")
            }
            .sheet(isPresented: $isShowingSheet1) {
                Sheet1()
            }
        }
    }

또는 2. 두 장의 시트를 병렬로 사용합니다.

    struct SheetTester: View {
        @State private var isShowingSheet1 = false
        var body: some View {
                Button(action: {
                    isShowingSheet1.toggle()
                }) {
                    Text("Show Sheet1")
                }
                .sheet(isPresented: $isShowingSheet1) {
                    Text("Sheet1")
                    Button {
                        isShowingSheet1.toggle()
                        isShowingSheet2.toggle()
                    } label: {
                        Text("Show Sheet2")
                    }
                }
                .sheet(isPresented: $isShowingSheet2) {
                    Text("Sheet2")
                }
            }
        }
    }

저는 당신에게 솔직하게 말할 것이고 그렇게 쉬운 해결책을 상상합니다.지금까지와 같이 시트를 넣은 후 이 시트 안에 텍스트를 넣고(필수 없음) 그 안에 다른 시트를 작성한 후 두 번째 부울을 사용하여 다른 시트를 엽니다.마트리오스카처럼요

이 파티에는 조금 늦었지만, 지금까지의 답변 중 View Model이 작업을 수행할 가능성에 대해서는 언급하지 않았습니다.나는 스위프트의 전문가가 아니기 때문에UI(매우 새로운 UI)는 더 나은 방법이 있을 수 있지만, 제가 도달한 솔루션은 다음과 같습니다.

enum ActiveSheet: Identifiable {
    case first
    case second
        
    var id: ActiveSheet { self }
}

struct MyView: View {

    @ObservedObject private var viewModel: MyViewModel

    private var activeSheet: Binding<ActiveSheet?> {
        Binding<ActiveSheet?>(
            get: { viewModel.activeSheet },
            set: { viewModel.activeSheet = $0 }
        )
    }

    init(viewModel: MyViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {

        HStack {
            /// some views
        }
        .onTapGesture {
            viewModel.doSomething()
        }
        .sheet(item: activeSheet) { _ in
            viewModel.activeSheetView()
        }
    }
}

...viewModel에서 -

    @Published var activeSheet: ActiveSheet?

    func activeSheetView() -> AnyView {
        
        switch activeSheet {
        case .first:
            return AnyView(firstSheetView())
        case .second:
            return AnyView(secondSheetView())
        default:
            return AnyView(EmptyView())
        }
    }

    // call this from the view, eg, when the user taps a button
    func doSomething() {
        activeSheet = .first // this will cause the sheet to be presented
    }

여기서 firstSheetView()와 secondSheetView()는 필요한 액션을 제공합니다.시트 내용

모든 비즈니스 로직이 시야에 들어오지 않게 하기 때문에 저는 이 접근방식이 마음에 듭니다.

언급URL : https://stackoverflow.com/questions/58837007/multiple-sheetispresented-doesnt-work-in-swiftui

반응형