CustomModalView 1.0.0

CustomModalView 1.0.0

Jan Kaltoun 维护。



  • jankaltoun

Custom Modal View

Custom Modal View 是一个轻量级的 SwiftUI 库,它可以让您展示完全可定制的模态对话框。

它的 API 构造与原生 .sheet(...) 视图修饰符类似,其使用方式很好地融入了现有的 SwiftUI 应用程序中。

安装

Swift Package Manager

只需将此仓库的 URL 添加到您的依赖项中。

https://github.com/jankaltoun/custom-modal-view

Cocoapods

如果您要通过 Cocoapods 使用此库,请将以下行添加到您的 Podfile 中。

pod 'CustomModalView'

平台支持

此库最适合在iOS和iPadOS上使用,但macOS也能正常工作。

代码也可以为tvOS和watchOS编译,但那里的UI范式有所不同,因此使用模态视图并不合适。

用法

请参阅示例项目了解iOS和macOS的示例。

创建模态视图

要创建模态视图,请在任何视图中使用 .modal(...) 视图修饰符。

您需要传递一个绑定到某个 BoolIdentifiable? 属性,以便模态知道何时显示和消失。其行为与 .sheet(...) 视图修饰符完全相同。

以下示例显示了绑定到 Bool 属性的初始化。

struct MainView: View {
    @State var modalIsDisplayed = false
    
    var body: some View {
        Button(action: { self.modalIsDisplayed = true }) {
            Text("Show modal")
        }
        .modal(isPresented: $modalIsDisplayed) {
            Text("Hello world!")
                .padding()
        }
    }
}

下一个示例显示了绑定到 Identifiable? 属性的初始化。

struct ContentView: View {
    enum SomethingIdentifiable: Int, Identifiable {
        case llama = 1

        var id: Int {
            rawValue
        }
    }
    
    @State var somethingIdentifiable: SomethingIdentifiable? = nil

    var body: some View {
        Button(action: { self.somethingIdentifiable = .llama }) {
            Text("Show modal")
        }
        .modal(item: $somethingIdentifiable) { item in
            Text("Alpacas are the best!")
                .padding()
        }
    }
}

关闭模态视图

默认情况下,点击模态视图后面的区域可以关闭它。此行为可以在(见模态视图样式部分)中修改。

如果您希望使用按钮关闭模态视图,可以使用 .modalPresentationMode 键来获取绑定到 ModalPresentationMode 的绑定并调用 dismiss()

您的视图应将该属性定义为 @Environment(\.modalPresentationMode) var modalPresentationMode: Binding<ModalPresentationMode>

示例如下。

import CustomModalView

struct ContentView: View {
    @State var modalIsDisplayed = false

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

struct DetailView: View {
    @Environment(\.modalPresentationMode) var modalPresentationMode: Binding<ModalPresentationMode>
    
    var body: some View {
        VStack(spacing: 32) {
            Text("I'm a modal")
            
            Button(action: {
                self.modalPresentationMode.wrappedValue.dismiss()
            }) {
                Text("Dismiss")
            }
        }
        .padding()
    }
}

另一种选择是仅将绑定传递给绑定模态视图的属性,并将它设置为 false(或如果您的属性是 Identifiable?,则为 nil)。

这有点麻烦,但可以很容易地完成。

struct ContentView: View {
    @State var modalIsDisplayed = false

    var body: some View {
        Button(action: { self.modalIsDisplayed = true }) {
            Text("Show modal")
        }
        .modal(isPresented: $modalIsDisplayed) {
            DetailView(isDisplayed: $modalIsDisplayed)
        }
    }
}

struct DetailView: View {
    @Binding var isDisplayed: Bool
    
    var body: some View {
        VStack(spacing: 32) {
            Text("I'm a modal.")
                .fixedSize(horizontal: false, vertical: true)
            
            Button(action: { self.isDisplayed = false }) {
                Text("Dismiss")
            }
        }
        .padding()
    }
    
    init(isDisplayed: Binding<Bool>) {
        self._isDisplayed = isDisplayed
    }
}

样式化模态视图

您可以随意设计自己的模态视图,并且有两个工具可供您使用:

  • 样式化视图
  • 样式化容器

视图样式化

视图样式化可以用来修改模态内容的显示效果。您可以使用所有 SwiftUI 工具来做到这一点。

例如,像以下示例中定义的模态。

struct ContentView: View {
    @State var modalIsDisplayed = false

    var body: some View {
        Button(action: { self.modalIsDisplayed = true }) {
            Text("Show modal")
        }
        .modal(isPresented: $modalIsDisplayed) {
            Text("I'm a fancy Hello world!")
                .frame(width: 200, height: 100, alignment: .topLeading)
                .padding()
                .background(Color.red)
                .foregroundColor(.white)
        }
    }
}

将产生以下用户界面。

容器样式化

如果您需要调整角半径、模态背景或基本内容,则可以创建自己的模态样式。

定义自己的样式非常简单。首先创建一个遵循 ModalStyle 协议的 struct,然后实现两个必需的函数。

使用这两个函数,您可以完全自定义背景和模态视图的外观,以及模态出现和消失时使用的动画。

struct DefaultModalStyle: ModalStyle {
    let animation: Animation? = .easeInOut(duration: 0.5)
    
    func makeBackground(configuration: ModalStyle.BackgroundConfiguration, isPresented: Binding<Bool>) -> some View {
        configuration.background
            .edgesIgnoringSafeArea(.all)
            .foregroundColor(.black)
            .opacity(0.3)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .zIndex(1000)
            .onTapGesture {
                isPresented.wrappedValue = false
            }
    }
    
    func makeModal(configuration: ModalStyle.ModalContentConfiguration, isPresented: Binding<Bool>) -> some View {
        configuration.content
            .background(Color.white)
            .clipShape(RoundedRectangle(cornerRadius: 16))
            .zIndex(1001)
    }
}

事实上,这正好是 DefaultModalStyle 实现的方式。

一种花哨的样式可能看起来像下面的例子。

import CustomModalView

struct FancyModalStyle: ModalStyle {
    let animation: Animation? = .easeInOut(duration: 0.5)
    
    func makeBackground(configuration: ModalStyle.BackgroundConfiguration, isPresented: Binding<Bool>) -> some View {
        configuration.background
            .edgesIgnoringSafeArea(.all)
            .foregroundColor(.blue)
            .opacity(0.3)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .zIndex(1000)
    }
    
    func makeModal(configuration: ModalStyle.ModalContentConfiguration, isPresented: Binding<Bool>) -> some View {
        configuration.content
            .background(Color.yellow)
            .clipShape(RoundedRectangle(cornerRadius: 8))
            .zIndex(1001)
    }
}

struct ContentView: View {
    @State var modalIsDisplayed = false
    
    var body: some View {
        Button(action: { self.modalIsDisplayed = true }) {
            Text("Show modal")
        }
        .modal(isPresented: $modalIsDisplayed) {
            Text("Hello world!")
                .padding()
        }
        .modalStyle(FancyModalStyle())
    }
}

该样式的结果如下截图所示。

作者

Jan Kaltoun, [email protected]

许可

请随意使用此代码。

如果您链接到此存储库或提及我是作者,我会很高兴!