Say Hello to MegaX

有的时候会有很多想法和点子,开了很多的项目,发现很多代码都是重复的,比如每个项目中我都会创建一个 if-else 的 View Modifier、BlurView 这些,最近我对于 CameraView 的使用也更加频繁了。

So, why not create a framework that gathers everything we will need in our development proccess?

Sure. We can.

于是,我和自己一拍即合,就开始找寻自己曾经开发过的一些可以复用的组件,对其改造甚至是重写,使其可以适用于不同的场景。

321,上链接:https://github.com/LiYanan2004/MegaX

So, what should we call it?

Well, that brings us to our legendary crack marketing team.

Sorry,串台了。

CameraView

在过年期间,我花了一个礼拜时间写了一个 System-like CameraView,用于拍摄静态图片,相信也是一个很常见的需求。

支持快速快门优先的响应式拍摄。

这里是一个简单的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SwiftUI
import MegaX

@main
struct MyCameraApp: App {
// This is needed, or the orientation behavior may be wired.
@UIApplicationDelegateAdaptor(AppOrientationDelegate.self) private var delegate

var body: some Scene {
WindowGroup {
CameraView { photoCaptured in
print("photoData: \(photoCaptured)")

}
}
}

这里需要额外在 App 入口增加一个 AppOrientationDelegate 是因为在 iPhone 上需要限制在 CameraView 内只能以 portait 显示,从而保证 UI 能够始终保持在原地。

当完成拍摄、完成处理后会调用闭包,你可以在这里处理后续的保存、处理的任务。

CameraView UI

AsyncButton

有时候,我们会在 Button 中使用异步操作来执行耗时任务,同时伴随需要展示 ProgressView

使用 MegaX 后,一切都变得简单起来。

1
2
3
4
5
AsyncButton("Download", systemName: "arrow.down.circle") {
await download()
} progress: {
ProgressView()
}

你可以像使用 Button 一样创建一个 AsyncButton

也可以额外创建自定义的 ProgressView Placeholder,默认就是:ProgressView()

Backdrop Blur Layer

这应该是一个重磅功能了。

SwiftUI 提供了原生的 Material 材质,但是除了模糊层之外还包含了CubedLuminanceMappingLayer(一种可以自适应深浅色模式的光照映射层)

在 MegaX 中,我通过大量实验(thanks to SwiftUI Preview)找到了一种很强大的调节方案,极大程度上的抵消掉了 CubedLuminanceMappingLayer,同时可以适应不同的背景

这样,我们就获得了”纯粹的“ Backdrop-Blur Layer

我知道 CAFilter 里有一个高斯模糊的滤镜,但是 CAFilter 是 Private API,无法完全确定是否可以通过审核。

MagaX 提供了纯粹的 SwiftUI 的解决方案,100% 过审核

1
2
YourView()
.backdropBlur()

这样会在视图下方增加一层模糊,模糊的内容是 YourView 下方的内容,而不是 YourView 本身

这是和 blur(radius:opaque:) 的本质区别

如果你正在构建一个 NavigationBar,你可以还需要处理 bar 和 content 之间的过度,这时候你可以使用 smoothEdges

1
2
CustomNavigationBar()
.backdropBlur(smoothEdges: .bottom)

这样就会在模糊层底部增加一个从 opaque 到 transparent 的蒙板

LongPressGesture with Location

我们很熟悉 SpatialTapGestureonTapGesture(action: (CGPoint) -> Void) API.

可惜的是,在我做 CameraView 时需要实现长按锁定对焦,也需要获取到点按的位置,于是就有了 SpatialLongPressGesture

[!Tip] 但是这里有一点需要特别注意:你需要使用 onChanged 来获取到争取的状态,而不是 onEnded

1
2
3
4
5
6
7
8
9
10
11
12
YourView()
.gesture(
SpatialLongPressGesture()
.onChanged { point in
guard let point else {
// Touch recognized.
return
}
// Long Press recognized with the location.
// Some actions here...
}
)

也可以使用 View Modifier 的版本:onLongPressGesture(minimumDuration:maximumDistance:coordinateSpace:perform:)

1
2
3
4
YourView()
.onLongPressGesture { point in
// Long Press recognized with location.
}

More

  • if-else modifier

    请谨慎地使用这个修改器,因为它会导致性能问题以及奇怪的动画。

    当且仅当 condition 值在整个 View LifeCycle 内不会变化时使用该修改器。

    举个栗子:在 iPad 环境中额外需要添加一个 padding

    1
    2
    3
    4
    5
    6
    7
    8
    import MegaX

    @Environment(\.deviceType) var deviceType

    YourView()
    .if(deviceType == .pad) { content in
    content.padding()
    }

    在这个例子中,在整个 View LifeCycle 中,deviceType 不会变化,可以放心使用这个修改器。

  • DeviceType environment value.

    mactvvisionwatch 基本都是在编译期(Native)就确定了

    iPad、iPhone、CarPlay、Mac Catalyst 会在运行时获取(UIDevice.current.userInterfaceIdiom)

    需要注意的是,macMac Catalyst是两种 DeviceType

Ending

这个库目前还是 WIP 状态,会不断迭代更新,希望它可以帮助到正在使用 SwiftUI 开发的开发者们。

作者

LiYanan

发布于

2024-02-18

更新于

2024-02-18

许可协议

评论