MinimizableView (iOS 13+ / iPadOS)
MinimizableView 是一个简单的 SwiftUI 视图,用于 iOS 和 iPadOS,可以像 Spotify 或 Apple Music 应用中的迷你播放器一样最小化。目前,SwiftUI 似乎不支持自定义模态对话框(具有与 sheet、actionSheet、alert、popover 等不同的动画/状态),因此这个简单的视图可以被视为一种解决方案。它只能从 iOS 13.0 使用,因为 SwiftUI 不在以前的 iOS 版本中使用。
版本 2.0 中的破坏性更改。请查看以下版本历史中的详细信息
特别感谢 Kavsoft (此处查看) - 我使用了示例中部分 MiniPlayer 内容。尽管如此,框架是我自己的创新。
示例项目
此仓库仅包含 Swift 包,不包含示例代码。请在此处下载示例项目 这里。您需要通过 CocoaPods 或 Swift 包管理器添加 MinimizableView 包(见下文 - 安装)。
特性
-
创建您自己的内容、背景和紧凑视图。紧凑视图是可选的 - 如果在初始化器中设置,将出现在最小化状态下。
-
通过更改 MinimizableViewHandler 的设置属性,您可以自定义以下属性
- minimizedHeight
- overrideHeight(如果您想设置与几何大小高度不同的高度)
- lateralMargin
- edgesIgnoringSafeAreas
查看示例以获取详细信息。
安装
推荐使用Swift Package Manager (SPM) 或 cocoapods 进行安装。
SPM:选择您的项目(不是目标),然后选择Swift Packages选项卡。点击+并输入MinimizableView - SPM应在GitHub上找到此包。
Cocoapods
platform :ios, '14.0'
target '[project name]' do pod 'MinimizableView' end
查看下面的版本历史记录以获取当前版本。
确保在您使用MinimizableView或MinimizableViewHandler的每个文件中导入MinimizableView。
import MinimizableView
使用方法
查看以下示例。此存储库只包含Swift包,没有示例代码。请在此处下载示例项目链接。
代码示例:内容视图(您的主视图)
简单地将.minimizableView修饰符附加到您的主视图,例如TabView。要触发显示、关闭、最小化和展开,您需要调用MinimizableViewHandler的相关功能:present()、dismiss()、minimize()和expand()。建议在每次使用tapGesture切换展开状态时,在minimizableViewHandler上调用toggleExpansionState()。
如果您不需要紧凑视图,只需传入EmptyView。在MinimizableView体中检查compactView是否为EmptyView,然后不显示它。如果没有紧凑视图,最小化状态时内容顶部将显示在屏幕底部。使用minimizableViewHandler作为EnvironmentObject在您的内容视图中 - 例如,在最小化属性更改时一次性移除和插入特定的子视图。
您还需要将minimizableViewHandler作为环境对象附加到MinimizableView。
struct RootView: View {
@ObservedObject var miniHandler: MinimizableViewHandler = MinimizableViewHandler()
@State var selectedTabIndex: Int = 0
@Namespace var namespace
var body: some View {
GeometryReader { proxy in
TabView(selection: self.$selectedTabIndex) {
Button(action: {
print(proxy.safeAreaInsets.bottom)
self.miniHandler.present()
}) { TranslucentTextButtonView(title: "Launch Minimizable View", foregroundColor: .green, backgroundColor: .green)}.disabled(self.miniHandler.isPresented)
.tabItem {
Image(systemName: "chevron.up.square.fill")
Text("Main View")
}.tag(0)
Text("More stuff").tabItem {
Image(systemName: "dot.square.fill")
Text("2nd View")
}.tag(1)
ListView(availableWidth: proxy.size.width)
.tabItem {
Image(systemName: "square.split.2x1.fill")
Text("List View")
}.tag(2)
}.background(Color(.secondarySystemFill))
.statusBar(hidden: self.miniHandler.isPresented && self.miniHandler.isMinimized == false)
// if you want a separate compactView, replace EmptyView() by some custom view. It will appear above the top part of your contentView, so make sure the compact view has a background colour.
.minimizableView(content: {ContentExample(animationNamespaceId: self.namespace)}, compactView: {EmptyView()}, backgroundView: {
VStack(spacing: 0){
BlurView(style: .systemChromeMaterial)
if self.miniHandler.isMinimized {
Divider()
}
}.cornerRadius(self.miniHandler.isMinimized ? 0 : 20)
.onTapGesture(perform: {
if self.miniHandler.isMinimized {
self.miniHandler.expand(
////alternatively, override the default animation. self.miniHandler.expand(animation: Animation)
}
})
}, dragOnChanged: { (value) in
self.dragOnChanged(value: value)
}, dragOnEnded: { (value) in
self.dragOnEnded(value: value)
}, geometry: proxy, settings: MiniSettings(minimizedHeight: 80))
.environmentObject(self.miniHandler)
}
//
}
func dragOnChanged(value: DragGesture.Value) {
if self.miniHandler.isMinimized == false { // expanded state
if value.translation.height > 0 {
self.miniHandler.draggedOffsetY = value.translation.height
}
} else {// minimized state
if value.translation.height < 0 {
self.miniHandler.draggedOffsetY = value.translation.height
}
}
}
func dragOnEnded(value: DragGesture.Value) {
if self.miniHandler.isMinimized == false {
if value.translation.height > 60 {
self.miniHandler.minimize()
}
} else {
if value.translation.height < -60 {
self.miniHandler.expand()
}
}
withAnimation(.spring()) {
self.miniHandler.draggedOffsetY = 0
}
}
}
变更日志
版本 2.2
iOS 15 更新:由于 miniView 上的动画修饰符可能影响内容视图子视图的动画,从而导致奇怪的行为,因此已从设置中移除了动画。现在,miniView 处理函数 present()、expand()、minimize() 和 toggleExpansionState() 包含了可以覆盖的动画参数(默认为 .spring())。
版本 2.1.1
iOS 15 更新:修复了在最小化状态下向上拖动 miniView 可能导致无限循环的bug。
版本 2.1
在设置中添加了 edgesIgnoringSafeArea。默认值是 [.bottom, .top]。如果需要,请确保为内容添加顶部填充。
版本 2.0.2
bug 修复:在最小化状态下且键盘出现时,miniView 现在将在键盘隐藏后正确地消失和重新出现。
版本 2.0.1
将 minimizedBottomMargin 移至 miniView 初始化器。这在例如根据屏幕方向改变底部边距时非常有用。
版本 2.0
破坏性更改。以下参数需要在初始化器中设置
- backgroundView
- onDragChanged 和 onDragEnded
- settings(可选)
版本 1.2.1
错误修复:当处于最小化状态时,如果键盘显示(而不是悬浮在键盘上方),迷你视图将消失。
版本 1.2
compactView 参数不能为 nil。如果您不想有单独的 compactView,请传递 EmptyView。从 minimizableView body(contentView 和 compactView)中删除过渡效果。相反,将过渡修饰符附加到您的 contentView 和 compactView 实现上。有关详细信息,请查看示例存储库。MiniSettings 结构体的参数现在可以在初始化器中直接设置。
版本 1.1.1
动画略有改进。
版本 1.1
内容视图仅在迷你视图呈现时出现。其他一些小改进。
版本 1.0
初始化器改动:内容视图和紧凑视图现在需要插入到闭包中,不再需要转换为 AnyView!错误修复:在没有物理主按钮的设备上(例如 iPhone 11 max),最小视图在隐藏状态下不再显示顶部。额外功能:便利修改器(见示例)。
版本 0.3.2
错误修复:onMinimization 现在按预期调用。仅在 isPresented 为 true 时调用 onExpansion。
版本 0.3.1
当可最小化视图的呈现状态为 false 时,在 offsetY 中添加安全边距 - 这修复了屏幕底部阴影可见性的错误。
版本 0.3
通过 VerticalDragGesture 修改器的收缩/扩展现在只在拖动结束时触发。VerticalDragGesture 视图修改器现在是框架内部的 - 相反,使用 修改器函数 verticalDragGesture(translationHeightTriggerValue: CGFloat)。
版本 0.2.1
更新 frameHeight 和 offsetY 函数以允许在向上拖动时展开最小化框架。
版本 0.2
首次公开发行。
作者
许可协议
MinimizableView 在 MIT 许可协议下可用。有关更多信息,请参阅 LICENSE 文件。