Sheet

  • 运用Sheet-Present一个新视图
  • 运用Sheets多重present
  • Sheet的视图怎么dismiss
  • 显现Popover视图
  • 避免Sheet被dismiss
  • 显现一个bottom-sheet
  • 怎么展现白画面的占位图

概述

文章首要共享SwiftUI Modifier的学习过程,将运用事例的办法进行说明。内容深入浅出,Sheet展现部分调试成果,不过测试代码是齐全的。假如想要运行成果,能够移步Github下载code -> github事例链接

1、运用Sheet-Present一个新视图

SwiftUI的Sheets用于视图上present新视图,一起仍然答应用户在准备好时向下拖动以封闭新视图。要运用sheet,给他一些东西来显现(文本、图片、自界说View等),添加一个Bool值来界说DetailView是否应该被显现,然后将其作为模态sheet附加到主视图上。

//例如,创立一个简单的DetailView,然后在点击Button时从ContentView-present它
struct SheetView: View {
    @Environment(\.dismiss) var dismiss
    var body: some View {
        Button("Press to dismiss") {
            dismiss()
        }
        .font(.title)
        .padding()
        .background(.black)
    }
}
struct FFPresentSheets: View {
    @State private var showingSheet = false
    var body: some View {
        Button("Show Sheet") {
            showingSheet.toggle()
        }
        .sheet(isPresented: $showingSheet) {
            SheetView()
        }
    }
}

假如在iOS14以下,运用@environment(.presentationMode) var presentationMode 和presentationMode.wrappedValue.dismiss()来替代。与导航push不同,Sheet不需求NavigationStack。 在iOS上,假如想封闭向下拖动封闭的操作,运用fullScreenCover()润饰符。

2、运用Sheets多重present

假如想在SwiftUI中显现多个View页面,通过从第一个View-present第二个视图来完成,不能够将两个sheet()润饰符一起附加到一个父视图上。

struct FFPresentSheetsMultiple: View {
    @State private var showingFirst = false
    @State private var showingSecond = false
    var body: some View {
        VStack {
            Button("Show First Sheet") {
                showingFirst = true
            }
        }
        .sheet(isPresented: $showingFirst) {
            Button("Show Second Sheet") {
                showingSecond = true
            }
            .sheet(isPresented: $showingSecond) {
                Text("Second Sheet")
            }
        }
    }
}

运用这种办法,两个Sheet都将正确显现。假如把两个Sheet()润饰符放在同一个父元素中,SwiftUI会失效,只显现第一个sheet。

调试成果

SwiftUI基础篇Sheet
SwiftUI基础篇Sheet
SwiftUI基础篇Sheet

3、Sheet的视图怎么dismiss

当运用Sheet显现了一个SwiftUI视图时,通常想要在某些工作完成后封闭那个视图。例如当用户点击一个按钮时。在SwiftUI中有两种处理这个问题的办法。

3.1、@Environment(.dismiss)

第一个办法是告诉视图运用它自己的Presentation mode的环境变量来封闭。任何视图都能够封闭自己,不管它是怎么present的。运用@Environment(\.dismiss)

struct DismissingView_015_01: View {
    @Environment(\.dismiss) var dismiss
    var body: some View {
        Button("Dismiss me") {
            dismiss()
        }
    }
}
struct FFSheetViewDismiss: View {
    @State private var showingDetail = false
    @State private var showingDetail2 = false
    var body: some View {
        Button("Show Detail") {
            showingDetail = true
        }
        .sheet(isPresented: $showingDetail) {
            DismissingView_015_01()
        }
    }
}

3.2、@Binding state

别的一种挑选是将绑定传递到所显现的Detail视图中,这样就能够将绑定值更改回false。仍然需求在sheet视图中拥有某种state属性,但现在它作为绑定传递给Detail视图。运用这种办法,视图将其绑定设置为false也会更新原始视图中的状况,导致Detail视图Dismiss

struct DismissingView_015_02: View {
    @Binding var isPresented: Bool
    var body: some View {
        Button("Dismiss me") {
            isPresented = false
        }
    }
}
struct FFSheetViewDismiss: View {
    @State private var showingDetail = false
    @State private var showingDetail2 = false
    var body: some View {
        Button("Show Detail2") {
            showingDetail2 = true
        }
        .sheet(isPresented: $showingDetail2) {
            DismissingView_015_02(isPresented: $showingDetail2)
        }
    }
}

3、fullScreenCover-present全屏视图

当想要全屏的时候,SwiftUI的fullScreenCover()润饰符供给了一样显现办法,在代码中,他的工作原理简直与Sheet一样。常规的sheet能够通过向下拖动来封闭,但是运用fullScreenCover-present的视图是不能的,因此,供给一种办法来dismiss新视图是要处理的问题。fullScreenCover()在macOS上不可用。

struct FullScreenModalView_015: View {
    @Environment(\.dismiss) var dismiss
    var body: some View {
        ZStack {
            Color.primary.ignoresSafeArea(edges: .all)
            Button("Dismiss modal") {
                dismiss()
            }
        }
    }
}
struct FFSheetFullScreenCover: View {
    @State private var isPresented = false
    var body: some View {
        Button("Present!") {
            isPresented.toggle()
        }
        .fullScreenCover(isPresented: $isPresented, content: FullScreenModalView_015.init)
    }
}

4、显现Popover视图

SwiftUI有一个专门的润饰器来显现弹出窗口,在iPad上显现为漂浮的气泡,在iOS上是滑动。要显现弹出窗口,需求一些状况来确定弹出窗口是否可见。与alert不同,弹出窗口能够包含任何类型的视图,所以,只要把需求的东西放在弹出窗口中就能够。

struct FFSheetPopover: View {
    @State private var showingPopover = false
    var body: some View {
        Button("Show Menu") {
            showingPopover = true
        }
        .popover(isPresented: $showingPopover) {
            Text("Your content here")
                .font(.headline)
                .padding()
        }
    }
}

5、避免Sheet被dismiss

SwiftUI供给了interactiveDismissDisabled()润饰符来操控用户是否能够向下滑动来封闭一个Sheet View。例如,假如用户必须接受的协议,只用同意才干封闭。

5.1、interactiveDismissDisabled()

最简单的办法是,interactiveDismissDisabled()润饰符附加到你的Sheet Content上。

struct ExampleSheet_015_01: View {
    @Environment(\.dismiss) var dismiss
    var body: some View {
        VStack {
            Text("Sheet View")
            Button("Dismiss", action: close)
        }
        .interactiveDismissDisabled()
    }
    func close() {
        dismiss()
    }
}
struct FFSheetSwipe: View {
    @State private var showingSheet = false
    @State private var showingSheet1 = false
    var body: some View {
        Button("Show Sheet") {
            showingSheet.toggle()
        }
        .sheet(isPresented: $showingSheet, content: ExampleSheet_015_01.init)
    }
}

5.2、设定dismiss的条件

能够将一个bool值绑定到润饰符,以答应swipe仅在成功满意某些条件时才撤销。

struct ExampleSheet_015_02: View {
    @State private var termsAccepted = false
    var body: some View {
        VStack {
            Text("Terms and conditions")
                .font(.title)
            Text("Lots of legalese here")
            Toggle("Accept", isOn: $termsAccepted)
        }
        .padding()
        .interactiveDismissDisabled(!termsAccepted)
    }
}
struct FFSheetSwipe: View {
    @State private var showingSheet1 = false
    var body: some View {
        Button("Show Sheet 1") {
            showingSheet1.toggle()
        }
        .sheet(isPresented: $showingSheet1, content: ExampleSheet_015_02.init)
    }
}

6、显现一个bottom-sheet

SwiftUI的presentationsDetents()润饰符能够创立从视图底部向上滑动的Sheet,能够不占满全屏,至于多少,依据需求来操控。

6.1、presentationDetents([.medium, .large])

一起支撑.medium和.large,SwiftUI将创立一个调整巨细的句柄,让用户在这两个size之间调整表单

struct FFSheetShowBottom: View {
    @State private var showingCredits = false
    var body: some View {
        Button("Show Credits") {
            showingCredits.toggle()
        }
        .sheet(isPresented: $showingCredits) {
            Text("一起支撑.medium和.large")
                .presentationDetents([.medium, .large])
        }
    }
}

6.2、presentationDragIndicator(.hidden)

躲藏提示条

struct FFSheetShowBottom: View {
    @State private var showingCredits2 = false
    var body: some View {
        Button("Show Credits 2") {
            showingCredits2.toggle()
        }
        .sheet(isPresented: $showingCredits2) {
            Text("躲藏提示条")
                .presentationDetents([.medium, .large])
                .presentationDragIndicator(.hidden)
        }
    }
}

6.3、presentationDragIndicator(.hidden)

即使有自界说的显现控件,当有一个高度比较小的class时,sheet也会主动占有整个屏幕。比方,横向的iPhone。假如想支撑此场景,请确保供给一种办法来封闭sheet view。即使指定一个内置巨细外,还能够供给一个规模为0-1的自界说分数。例如,创立一个占用屏幕底部15%的sheet View

struct FFSheetShowBottom: View {
    @State private var showingCredits3 = false
    var body: some View {
        Button("Show Credits 3") {
            showingCredits3.toggle()
        }
        .sheet(isPresented: $showingCredits3) {
            Text("创立百分比的占用高度")
                .presentationDetents([.fraction(0.15)])
        }
    }
}

6.4、presentationDetents([.height(300)])

创立一个准确的高度

struct FFSheetShowBottom: View {
    @State private var showingCredits4 = false
    var body: some View {
        Button("Show Credits 4") {
            showingCredits4.toggle()
        }
        .sheet(isPresented: $showingCredits4) {
            Text("固定高度")
                .presentationDetents([.height(300)])
        }
    }
}

6.5、动态切换高度

能够依据需求给视图附加恣意多的detens,只要把他们悉数添加到detens的调集中,SwiftUI会主动处理。例如,能够让用户在从10%切换到100%

struct FFSheetShowBottom: View {
    @State private var showingCredits5 = false
    let heights = stride(from: 0.1, to: 1.0, by: 0.1).map { PresentationDetent.fraction($0) }
    var body: some View {
        Button("Show Credits 5") {
            showingCredits5.toggle()
        }
        .sheet(isPresented: $showingCredits5) {
            Text("动态切换高度")
                .presentationDetents(Set(heights))
        }
    }
}

调试成果

SwiftUI基础篇Sheet
SwiftUI基础篇Sheet
SwiftUI基础篇Sheet
SwiftUI基础篇Sheet
SwiftUI基础篇Sheet
SwiftUI基础篇Sheet

7、怎么展现白画面的占位图

SwiftUI有一个专用的ContentUnavailableView视图,在没有数据的时候向用户展现非空画面。例如,在用户执行了查找操作之后,并未查找到内容,运用此View。

7.1、基础款式

默认供给一个放大镜图标,由标题和副标题构成,用来展现用户并未查找到具体内容

struct FFContentUnavailable: View {
    var body: some View {
        ContentUnavailableView.search
    }
}

7.2、自界说文本

假如有需求,能够对其进行自界说文本,以添加用户查找的内容

struct FFContentUnavailableCustom: View {
    var body: some View {
        ContentUnavailableView.search(text: "Life, the Universe, and Everything")
    }
}

7.3、彻底自界说

彻底自界说所有的内容

struct FFContentUnavailableCustomAll: View {
    var body: some View {
        ContentUnavailableView("No favorites", systemImage: "star", description: Text("You don't have any favorites yet."))
            .symbolVariant(.slash)
    }
}

调试成果

SwiftUI基础篇Sheet
SwiftUI基础篇Sheet
SwiftUI基础篇Sheet