Skip to content

Commit

Permalink
添加视图协议内容
Browse files Browse the repository at this point in the history
  • Loading branch information
ming1016 committed May 23, 2024
1 parent dd44bbd commit 82bfec2
Show file tree
Hide file tree
Showing 26 changed files with 1,465 additions and 27 deletions.
112 changes: 94 additions & 18 deletions SwiftPamphletApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

33 changes: 24 additions & 9 deletions SwiftPamphletApp/Guide/View/GuideListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -370,17 +370,32 @@ final class GuideListModel {
L(t: "SwiftCharts"),
]),
L(t: "修饰符", sub: [

]),
L(t: "样式", sub: [

L(t: "自定义修饰符"),
L(t: "背景修饰符"),
L(t: "修饰符-visualEffect"),
L(t: "修饰符-圆角"),
L(t: "ContainerRelativeShape"),
L(t: "修饰符-fixedSize"),
L(t: "修饰符-蒙版"),
L(t: "redacted隐私展示"),
]),
L(t: "ViewBuilder", sub: [

]),
L(t: "视图协议", sub: [

L(t: "视图协议",sub: [
L(t: "视图协议-简介"),
L(t: "视图协议-核心协议"),
L(t: "Style协议"),
L(t: "小组件协议"),
L(t: "Shapes协议"),
L(t: "Animations协议"),
L(t: "视图协议-Environment"),
L(t: "Previews协议"),
L(t: "Legacy bridges协议"),
L(t: "Responder chain协议"),
L(t: "Toolbar协议"),
L(t: "Documents协议"),
L(t: "特定情况视图协议"),
]),
L(t: "SwiftUI-自定义样式"),
L(t: "ViewBuilder"),
L(t: "SwiftUI数据流")
]),
L(t: "SwiftData", icon: "swiftdata", sub: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@


要创建自定义按钮样式,我们需要创建一个新类型,该类型遵循 `ButtonStyle` 协议。

该协议都要求实现一个名为 `makeBody(configuration:)` 的方法。传递给此方法的配置参数包含了我们正在为其设置样式的按钮的相关信息。

我们可以通过 `configuration.label` 获取表示按钮标签的视图,并将我们的样式应用于该标签。

```swift
struct ContentView: View {
@State private var isPrivacyMode = true
var body: some View {
Button(action: {
print("按钮被点击!")
}) {
Text("点击我")
}
.buttonStyle(ComicButtonStyle())
}
}

struct ComicButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
Spacer()
configuration.label
Spacer()
}
.padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
.font(.title).bold()
.background {
Capsule()
.stroke(configuration.isPressed ? .red : .blue, lineWidth: 2)
}
.opacity(configuration.isPressed ? 0.6 : 1)
}
}
```
117 changes: 117 additions & 0 deletions SwiftPamphletApp/Resource/Guide/SwiftUI/ViewBuilder(ap).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@


`@ViewBuilder` 是 SwiftUI 中的一个特性,它允许你在函数或计算属性中构建和返回一个或多个视图。这使得你可以创建复杂的视图层次结构,同时保持代码的可读性和简洁性。

```swift
struct ContentView: View {
@State private var isPrivacyMode = true
var body: some View {
MovieView(title: "红高粱", poster: Image("evermore"))
}
}

struct MovieView: View {
var title: String
var poster: Image

var body: some View {
VStack {
poster
.resizable()
.scaledToFit()
Text(title)
.font(.title)
.foregroundColor(.yellow)
.padding(.top, 8)
footer()
}
.background(Color.black)
.cornerRadius(10)
.padding()
}

@ViewBuilder
func footer() -> some View {
HStack {
Text("导演: 张艺谋")
.font(.caption)
.foregroundColor(.gray)
Spacer()
Text("评分: 9.0")
.font(.caption)
.foregroundColor(.gray)
}
.padding(.horizontal)
}
}
```

`@ViewBuilder` 的一些主要的功能:

- 支持 `if let``if case`
- 支持在 'if' 语句中使用多个布尔条件
- 支持 `switch` 语句
- 支持 `if #available` 语句
- 支持处理 `#warning``#error`
- 支持 `let`/`var` 声明

接下来,我们将使用 `@ViewBuilder` 来创建一个可展开的电影卡片视图。其中会用到上面提到的条件判断的功能。

```swift
struct ContentView: View {
@State private var isPrivacyMode = true
var body: some View {
MovieCard(poster: Image("evermore"), title: "红高粱", director: "张艺谋", rating: 9.0) {
Text("红高粱是一部由张艺谋执导的电影,讲述了九十年代的中国农村生活。")
.foregroundColor(.white)
.padding()
}
}
}

struct MovieCard<Content: View>: View {
let poster: Image
let title: String
let director: String
let rating: Double
@ViewBuilder var content: () -> Content
@State private var isExpanded = false

var body: some View {
VStack {
HStack {
poster
.resizable()
.scaledToFit()
.frame(width: 100, height: 150)
VStack(alignment: .leading) {
Text(title)
.font(.title2)
.foregroundColor(.yellow)
Text("导演: \(director)")
.font(.subheadline)
.foregroundColor(.gray)
Text("评分: \(rating, specifier: "%.1f")")
.font(.subheadline)
.foregroundColor(.gray)
}
.padding(.leading)
}
if isExpanded {
content()
.transition(.move(edge: .bottom))
}
}
.padding()
.background(Color.black)
.cornerRadius(10)
.onTapGesture {
withAnimation {
isExpanded.toggle()
}
}
}
}
```

在这个例子中,我们创建了一个 `MovieCard` 视图,其中包含了一个海报、标题、导演和评分。我们还添加了一个 `content` 属性,用于展示电影的简介。在 `MovieCard` 的主体中,我们使用了 `@ViewBuilder` 来创建一个可展开的电影卡片视图。当点击卡片时,电影的简介会展开或收起。
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

`ContainerRelativeShape` 是 SwiftUI 中的一个视图,它可以创建一个形状,这个形状会根据其包含的容器的形状进行变化。这意味着,如果你将 `ContainerRelativeShape` 用作视图的背景,那么这个背景的形状会自动适应视图的形状。

以下是一个简单的使用示例:

```swift
Text("Hello, World!")
.padding()
.background(ContainerRelativeShape().fill(Color.blue))
```

在这个示例中,`Text` 视图的背景是一个 `ContainerRelativeShape`,并且这个形状被填充了蓝色。由于 `ContainerRelativeShape` 会自动适应其容器的形状,所以这个背景的形状会自动适应 `Text` 视图的形状。

`ContainerRelativeShape` 能适应不同容器的形状。

以下代码展示怎么为 Widget 创建一个适应不同硬件容器显示不同形状边框的背景:

```swift
struct ContentView: View {
var body: some View {
ZStack {
Color(.yellow)

ContainerRelativeShape()
.inset(by: 10)
.fill(Color.indigo)
Image(systemName: "person.and.background.dotted")
.resizable()
.clipShape(ContainerRelativeShape()
.inset(by: 10))
Text("播放电影")
.padding()
.background(ContainerRelativeShape().fill(Color.white))
}
}
}
```

在这个示例中,我们使用 `ContainerRelativeShape` 来设置 `Color` 视图的背景,然后使用 `inset(by:)` 方法来设置 `ContainerRelativeShape` 的边距。最后,我们使用 `ContainerRelativeShape` 来设置 `Text` 视图的背景。




Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

## `.privacySensitive`

使用 .privacySensitive 修饰符,可以在锁屏时隐藏隐私信息,例如电话号码、电子邮件地址等。这样可以保护用户的隐私信息,避免在锁屏时泄露用户的隐私信息。

## `.redacted()`

`.redacted()` 是一个修饰符,用于将视图的内容替换为占位符。这在加载数据时显示占位符,或者在隐私敏感的情况下隐藏数据特别有用。

假设我们有一个显示电影海报的视图,当海报图片正在加载时,我们可以使用 `.redacted()` 修饰符来显示占位符:

```swift
struct MoviePosterView: View {
@State private var isLoading = true
var body: some View {
Image("movie_poster")
.resizable()
.scaledToFit()
.redacted(reason: isLoading ? .placeholder : [])
}
}
```

在这个例子中,当 `isLoading``true` 时,图片视图的内容将被替换为占位符。当海报图片加载完成,你可以将 `isLoading` 设置为 `false`,这将移除占位符并显示实际的图片。

你也可以使用 `.redacted()` 修饰符来隐藏隐私敏感的数据。例如,你可能有一个显示电影演员的视图,但在某些情况下,你希望隐藏这个信息:

```swift
struct MovieActorView: View {
@State private var isPrivacyMode = true
var body: some View {
Text("Movie Actor")
.redacted(reason: isPrivacyMode ? .placeholder : [])
}
}
```

在这个例子中,当 `isPrivacyMode``true` 时,文本视图的内容将被替换为占位符,从而隐藏电影演员的名字。当隐私模式关闭,你可以将 `isPrivacyMode` 设置为 `false`,这将移除占位符并显示电影演员的名字。
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

在 SwiftUI 中,`.fixedSize()` 修饰符的作用是使视图保持其理想的大小,而不是根据其父视图的大小和布局进行调整。这对于文本视图特别有用,因为默认情况下,文本视图会尽可能地扩展以填充其父视图的空间,这可能会导致文本被切断。使用 `.fixedSize()` 修饰符可以防止这种情况发生。

例如,以下代码创建了一个文本视图,该视图的宽度被限制为 100 点,但由于使用了 `.fixedSize()` 修饰符,所以文本视图会保持其理想的大小,而不会被切断:

```swift
Text("这是一段很长的文本,如果没有使用 .fixedSize() 修饰符,那么这段文本可能会被切断。")
.frame(width: 100)
.fixedSize()
```

在这个例子中,如果没有 `.fixedSize()` 修饰符,那么文本视图的宽度会被限制为 100 点,可能会导致文本被切断。但是由于使用了 `.fixedSize()` 修饰符,所以文本视图会保持其理想的大小,即使这意味着它的宽度会超过 100 点。

以下是一个更复杂的示例,展示了如何创建一个自定义的按钮样式,该样式使用 `.fixedSize()` 修饰符来限制按钮的大小,以便容纳每个按钮的理想大小:

```swift
struct CustomButtonStyle: ButtonStyle {
var backgroundColor: Color

func makeBody(configuration: Configuration) -> some View {
configuration.label
.textCase(.none)
.font(.system(size: 20))
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(backgroundColor, in: Capsule())
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
}
}

struct ContentView: View {
var body: some View {
VStack {
Button(action: {}) {
Text("播放")
}
.buttonStyle(CustomButtonStyle(backgroundColor: .red))

Button(action: {}) {
Text("查看详情")
}
.buttonStyle(CustomButtonStyle(backgroundColor: .green))

Button(action: {}) {
Text("添加到收藏")
}
.buttonStyle(CustomButtonStyle(backgroundColor: .blue))

Button(action: {}) {
Text("分享给朋友们观看")
}
.buttonStyle(CustomButtonStyle(backgroundColor: .orange))

Button(action: {}) {
Text("查看评论")
}
.buttonStyle(CustomButtonStyle(backgroundColor: .purple))
}
.fixedSize()
}
}
```

使用 `.fixedSize()` 会调整 `VStack` 的大小,以适应其子视图的理想大小,宽度会等于子视图中最宽的视图的宽度,以此达到子视图中每个按钮的宽度保持一致的效果。
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

`visualEffect` 是一个强大的修饰符,它允许开发者在不改变当前布局的前提下,对视图进行一些视觉效果的修改。这意味着,你可以在不影响视图的位置和大小的情况下,对视图进行一些视觉上的调整。

这个修饰符的工作方式是,它会在闭包中提供一个 `GeometryProxy` 对象,你可以使用这个对象来获取视图的一些几何信息,如位置和大小。然后,你可以在闭包中对视图应用一些特定的修饰符。

有很多种视图修饰符可以使用,如缩放、偏移、模糊、对比度、饱和度、不透明度、旋转等。这些都是视觉效果,现在都可以在 `visualEffect` 闭包中使用。

`visualEffect` 修饰符还支持动画。这意味着,你可以在修改视图的视觉效果的同时,添加一些动画效果,使视图的变化更加平滑和自然。

以下是一个示例:

```swift
struct ContentView: View {
@State private var isPlaying = false

var body: some View {
VStack {
Button("播放/暂停") {
isPlaying.toggle()
}
.padding()
.background(Color.indigo)
.foregroundColor(.white)
.cornerRadius(10)

Text(isPlaying ? "正在播放电影" : "电影已暂停")
.font(.title)
.foregroundColor(isPlaying ? .indigo : .gray)
.visualEffect { initial, geometry in
initial.scaleEffect(
CGSize(
width: isPlaying ? 1.5 : 1,
height: isPlaying ? 1.5 : 1
)
)
}
.animation(.easeInOut, value: isPlaying)
}
.padding()
}
}
```

以上代码中,我们创建了一个 `ContentView` 视图,其中包含一个按钮和一个文本。当点击按钮时,`isPlaying` 的值会切换。我们还在文本视图上使用了 `visualEffect` 修饰符,通过 `initial.scaleEffect` 方法来设置文本的缩放效果。我们还使用了 `animation` 修饰符,通过 `.easeInOut` 动画效果来实现缩放动画。



Loading

0 comments on commit 82bfec2

Please sign in to comment.