Skip to content

Commit

Permalink
新增 foreach 和 scrollview 内容
Browse files Browse the repository at this point in the history
  • Loading branch information
ming1016 committed May 13, 2024
1 parent 7dd1388 commit 4bc82f4
Show file tree
Hide file tree
Showing 14 changed files with 474 additions and 37 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Swift开发的手册,是个 macOS 程序,已上线 App Store [点击安装](

- 手册书签收藏
- 资料收集整理
- 离线保存资料
- 知识点和资料关联
- 手册、WWDC和资料可搜索
- Github 开发者和仓库信息添加管理
Expand Down
52 changes: 46 additions & 6 deletions SwiftPamphletApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@
08659BDD2BE9E3AA009B7C00 /* SwiftData-调试(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 08659BDC2BE9E3AA009B7C00 /* SwiftData-调试(ap).md */; };
08659BDF2BEA4D8C009B7C00 /* SwiftData-模型关系(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 08659BDE2BEA4D8C009B7C00 /* SwiftData-模型关系(ap).md */; };
0868D00B2BDD37280023C871 /* SMGitHub in Frameworks */ = {isa = PBXBuildFile; productRef = 0868D00A2BDD37280023C871 /* SMGitHub */; };
086923312BF171A6006779A3 /* ForEach(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 086923302BF171A6006779A3 /* ForEach(ap).md */; };
086923342BF178D9006779A3 /* 固定到滚动视图的顶部(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 086923332BF178D9006779A3 /* 固定到滚动视图的顶部(ap).md */; };
086923362BF18918006779A3 /* 滚动到特定的位置(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 086923352BF18918006779A3 /* 滚动到特定的位置(ap).md */; };
086923382BF19AB7006779A3 /* scrollTargetBehavior分页滚动(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 086923372BF19AB7006779A3 /* scrollTargetBehavior分页滚动(ap).md */; };
0869233A2BF1A490006779A3 /* scrollTransition视觉效果(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 086923392BF1A490006779A3 /* scrollTransition视觉效果(ap).md */; };
0869233C2BF1BF35006779A3 /* ScrollView-参考资料(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 0869233B2BF1BF35006779A3 /* ScrollView-参考资料(ap).md */; };
086A5F072744E88E00FECE02 /* SwiftPamphletAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086A5F062744E88E00FECE02 /* SwiftPamphletAppApp.swift */; };
086A5F0B2744E89100FECE02 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 086A5F0A2744E89100FECE02 /* Assets.xcassets */; };
086A5F0E2744E89100FECE02 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 086A5F0D2744E89100FECE02 /* Preview Assets.xcassets */; };
Expand Down Expand Up @@ -400,6 +406,12 @@
08659BDA2BE9A834009B7C00 /* SwiftData-版本迁移(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "SwiftData-版本迁移(ap).md"; sourceTree = "<group>"; };
08659BDC2BE9E3AA009B7C00 /* SwiftData-调试(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "SwiftData-调试(ap).md"; sourceTree = "<group>"; };
08659BDE2BEA4D8C009B7C00 /* SwiftData-模型关系(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "SwiftData-模型关系(ap).md"; sourceTree = "<group>"; };
086923302BF171A6006779A3 /* ForEach(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "ForEach(ap).md"; sourceTree = "<group>"; };
086923332BF178D9006779A3 /* 固定到滚动视图的顶部(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "固定到滚动视图的顶部(ap).md"; sourceTree = "<group>"; };
086923352BF18918006779A3 /* 滚动到特定的位置(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "滚动到特定的位置(ap).md"; sourceTree = "<group>"; };
086923372BF19AB7006779A3 /* scrollTargetBehavior分页滚动(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "scrollTargetBehavior分页滚动(ap).md"; sourceTree = "<group>"; };
086923392BF1A490006779A3 /* scrollTransition视觉效果(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "scrollTransition视觉效果(ap).md"; sourceTree = "<group>"; };
0869233B2BF1BF35006779A3 /* ScrollView-参考资料(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "ScrollView-参考资料(ap).md"; sourceTree = "<group>"; };
086A5F032744E88E00FECE02 /* 戴铭的开发小册子.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "戴铭的开发小册子.app"; sourceTree = BUILT_PRODUCTS_DIR; };
086A5F062744E88E00FECE02 /* SwiftPamphletAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftPamphletAppApp.swift; sourceTree = "<group>"; };
086A5F0A2744E89100FECE02 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -899,6 +911,27 @@
path = SwiftData;
sourceTree = "<group>";
};
086923322BF17780006779A3 /* Scroll滚动视图 */ = {
isa = PBXGroup;
children = (
08BE637327CCAB52002BC6A8 /* ScrollView(ap).md */,
086923332BF178D9006779A3 /* 固定到滚动视图的顶部(ap).md */,
086923352BF18918006779A3 /* 滚动到特定的位置(ap).md */,
086923372BF19AB7006779A3 /* scrollTargetBehavior分页滚动(ap).md */,
086923392BF1A490006779A3 /* scrollTransition视觉效果(ap).md */,
0869233B2BF1BF35006779A3 /* ScrollView-参考资料(ap).md */,
);
path = "Scroll滚动视图";
sourceTree = "<group>";
};
0869233D2BF1C2CF006779A3 /* List列表 */ = {
isa = PBXGroup;
children = (
08BE635327C63828002BC6A8 /* List(ap).md */,
);
path = "List列表";
sourceTree = "<group>";
};
086A5EFA2744E88E00FECE02 = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1009,8 +1042,9 @@
08A7FF2E2BEABE0100E12E5A /* 数据集合组件 */ = {
isa = PBXGroup;
children = (
08BE637327CCAB52002BC6A8 /* ScrollView(ap).md */,
08BE635327C63828002BC6A8 /* List(ap).md */,
086923302BF171A6006779A3 /* ForEach(ap).md */,
086923322BF17780006779A3 /* Scroll滚动视图 */,
0869233D2BF1C2CF006779A3 /* List列表 */,
08BE636327C886D2002BC6A8 /* LazyVStack和LazyHStack(ap).md */,
08BE636F27C8F6A7002BC6A8 /* LazyVGrid和LazyHGrid(ap).md */,
08026C502869B41B00792EF1 /* table(ap).md */,
Expand Down Expand Up @@ -1361,13 +1395,15 @@
08448F8C279EB84800B61353 /* 布局动画(ap).md in Resources */,
08448FC1279EC4B500B61353 /* filter(ap).md in Resources */,
08448F9C279EBA8200B61353 /* 闭包(ap).md in Resources */,
0869233C2BF1BF35006779A3 /* ScrollView-参考资料(ap).md in Resources */,
08448FAF279EC31200B61353 /* 不透明类型(ap).md in Resources */,
08449029279ECEB100B61353 /* SwiftUI是什么(ap).md in Resources */,
08448F79279EB68D00B61353 /* 随机(ap).md in Resources */,
08BE632A27BE220D002BC6A8 /* 全屏模式(ap).md in Resources */,
08448F5B279EA84100B61353 /* macOS共享菜单(ap).md in Resources */,
0844900A279ECB8C00B61353 /* combineLatest(ap).md in Resources */,
08026C4D2869B3A600792EF1 /* Transferable(ap).md in Resources */,
0869233A2BF1A490006779A3 /* scrollTransition视觉效果(ap).md in Resources */,
08659BC72BE8FD84009B7C00 /* SwiftUI数据流(ap).md in Resources */,
08449011279ECC3E00B61353 /* Combine KVO(ap).md in Resources */,
08448FE2279EC7CF00B61353 /* Nil-coalescing(ap).md in Resources */,
Expand Down Expand Up @@ -1420,6 +1456,7 @@
08659BCD2BE9A40A009B7C00 /* 创建@Model模型(ap).md in Resources */,
08449000279ECAE100B61353 /* flatMap(ap).md in Resources */,
08D8EFFE2BEFA3E300AA0020 /* 支持多个小组件(ap).md in Resources */,
086923382BF19AB7006779A3 /* scrollTargetBehavior分页滚动(ap).md in Resources */,
08448FAB279EC2B400B61353 /* 枚举(ap).md in Resources */,
08522BD627CF3218005FF059 /* Picker(ap).md in Resources */,
08659BD92BE9A80E009B7C00 /* SwiftData多线程(ap).md in Resources */,
Expand All @@ -1439,6 +1476,7 @@
085BB77627D22FE300E8F69A /* SwiftUI Canvas(ap).md in Resources */,
08BE635827C63F3A002BC6A8 /* ControlGroup(ap).md in Resources */,
08BE637427CCAB52002BC6A8 /* ScrollView(ap).md in Resources */,
086923362BF18918006779A3 /* 滚动到特定的位置(ap).md in Resources */,
08448F73279EB5DF00B61353 /* 文件(ap).md in Resources */,
08448FC8279EC54300B61353 /* If(ap).md in Resources */,
08448F7E279EB71D00B61353 /* 单例(ap).md in Resources */,
Expand Down Expand Up @@ -1484,6 +1522,7 @@
08026C4C2869B39F00792EF1 /* Advanced layout control(ap).md in Resources */,
086A5F0E2744E89100FECE02 /* Preview Assets.xcassets in Resources */,
08448F0F2799328700B61353 /* css_cn.html in Resources */,
086923312BF171A6006779A3 /* ForEach(ap).md in Resources */,
08448F5A279EA84100B61353 /* 三栏结构(ap).md in Resources */,
08448F9E279EBAA800B61353 /* 函数(ap).md in Resources */,
08448FEC279EC8BE00B61353 /* 注释(ap).md in Resources */,
Expand All @@ -1496,6 +1535,7 @@
08D8EFFA2BEF9C9800AA0020 /* 小组件-远程定时获取数据(ap).md in Resources */,
0858C5C72BEBD230004F4C04 /* ContentUnavailableView(ap).md in Resources */,
08448FD5279EC62700B61353 /* Sets(ap).md in Resources */,
086923342BF178D9006779A3 /* 固定到滚动视图的顶部(ap).md in Resources */,
08448F75279EB62B00B61353 /* Scanner(ap).md in Resources */,
08BE632C27BE3762002BC6A8 /* Link(ap).md in Resources */,
08448FE0279EC7AF00B61353 /* 三元(ap).md in Resources */,
Expand Down Expand Up @@ -1691,7 +1731,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 7;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"SwiftPamphletApp/Preview Content\"";
DEVELOPMENT_TEAM = 962Z8PV35L;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -1705,7 +1745,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 6.4.2;
MARKETING_VERSION = 6.4.3;
OTHER_LDFLAGS = (
"-Xlinker",
"-interposable",
Expand Down Expand Up @@ -1734,7 +1774,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 7;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"SwiftPamphletApp/Preview Content\"";
DEVELOPMENT_TEAM = 962Z8PV35L;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -1748,7 +1788,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 6.4.2;
MARKETING_VERSION = 6.4.3;
PRODUCT_BUNDLE_IDENTIFIER = com.starming.SwiftPamphletAppByMing;
PRODUCT_NAME = "戴铭的开发小册子";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
14 changes: 12 additions & 2 deletions SwiftPamphletApp/Guide/View/GuideListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,18 @@ final class GuideListModel {
L(t: "Image"),
]),
L(t: "数据集合组件", sub: [
L(t: "ScrollView"),
L(t: "List"),
L(t: "ForEach"),
L(t: "Scroll视图", icon: "scroll", sub: [
L(t: "ScrollView"),
L(t: "固定到滚动视图的顶部",icon: "pin.circle"),
L(t: "滚动到特定的位置"),
L(t: "scrollTargetBehavior分页滚动", icon: "book.pages"),
L(t: "scrollTransition视觉效果"),
L(t: "ScrollView-参考资料"),
]),
L(t: "List列表", icon: "list.bullet.rectangle.portrait.fill", sub: [
L(t: "List", icon: "list.bullet.rectangle.portrait"),
]),
L(t: "LazyVStack和LazyHStack"),
L(t: "LazyVGrid和LazyHGrid"),
L(t: "table"),
Expand Down
16 changes: 12 additions & 4 deletions SwiftPamphletApp/Guide/WWDC/WWDCDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,18 @@ struct WWDCDetailView: View {
}
VStack(alignment:.leading, spacing: 10) {
if let vurl = ss.media.videoOriginalUrl {
if ss.year > 2020 {
VideoPlayer(player: AVPlayer(url: URL(string: vurl)!))
} else {
Link("视频地址:\(vurl)", destination: URL(string: vurl)!)
if let okVurl = URL(string: vurl) {
if ss.year > 2020 {
#if arch(arm64)
VideoPlayer(player: AVPlayer(url: okVurl))
#elseif arch(x86_64)
Link("视频地址:\(vurl)", destination: okVurl)
#else
Link("视频地址:\(vurl)", destination: okVurl)
#endif
} else {
Link("视频地址:\(vurl)", destination: okVurl)
}
}
}
if let platforms = ss.platforms {
Expand Down
7 changes: 7 additions & 0 deletions SwiftPamphletApp/InfoOrganizer/Info/EditInfoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ struct EditInfoView: View {
private func textAndPreviewView() -> some View {
TextEditor(text: $info.des).border()
.padding(10)
.contentMargins(.all, 30, for: .scrollContent)
.tabItem { Label("文本", systemImage: "circle") }
.tag(1)
// WebUIView(html: wrapperHtmlContent(content: MarkdownParser().html(from: info.des)), baseURLStr: "")
Expand Down Expand Up @@ -339,6 +340,12 @@ struct EditInfoView: View {
.padding(.leading, 1)
}
.padding(2)
Button(action: {
showSheet = false
}, label: {
Label("关闭", systemImage: "xmark.circle")
})
.padding(2)
}
.padding(20)
})
Expand Down
6 changes: 6 additions & 0 deletions SwiftPamphletApp/InfoOrganizer/Info/InfoListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ struct InfoListView: View {
.padding(.leading, 1)
}
.padding(2)
Button(action: {
showSheet = false
}, label: {
Label("关闭", systemImage: "xmark.circle")
})
.padding(2)
}
.padding(20)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@

## 使用

在 SwiftUI 中,`ForEach` 是一个结构体,它可以创建一组视图,每个视图都有一个与数据集中的元素相对应的唯一标识符。这对于在列表或其他集合视图中显示数据非常有用。

以下视图集会用到 ForEach:

- List
- ScrollView
- LazyVStack / LazyHStack
- Picker
- Grids (LazyVGrid / LazyHGrid)

例如,如果你有一个 `BookmarkModel` 的数组,并且你想为每个书签创建一个文本视图,你可以这样做:

```swift
struct ContentView: View {
var bookmarks: [BookmarkModel]

var body: some View {
List {
ForEach(bookmarks) { bookmark in
Text(bookmark.name)
}
}
}
}
```

`ForEach` 遍历 `bookmarks` 数组,并为每个 `BookmarkModel` 对象创建一个 `Text` 视图。`bookmark` 参数是当前遍历的 `BookmarkModel` 对象。

`BookmarkModel` 必须遵循 `Identifiable` 协议,这样 SwiftUI 才能知道如何唯一地标识每个视图。在你的代码中,`BookmarkModel` 已经有一个 `id` 属性,所以你只需要让 `BookmarkModel` 遵循 `Identifiable` 协议即可:

```swift
final class BookmarkModel: Identifiable {
// your code here
}
```

## 使用索引范围进行编号

你可以使用 `ForEach` 结构体的另一个版本,它接受一个范围作为其数据源。这个范围可以是一个索引范围,这样你就可以为每个项目编号。

例如,如果你有一个 `BookmarkModel` 的数组,并且你想为每个书签创建一个文本视图,并在前面添加一个编号,你可以这样做:

```swift
struct ContentView: View {
var bookmarks: [BookmarkModel]

var body: some View {
List {
ForEach(bookmarks.indices, id: \.self) { index in
Text("\(index + 1). \(bookmarks[index].name)")
}
}
}
}
```

在这个例子中,`ForEach` 遍历 `bookmarks` 数组的索引,并为每个 `BookmarkModel` 对象创建一个 `Text` 视图。`index` 参数是当前遍历的索引。我们使用 `\(index + 1). \(bookmarks[index].name)` 来创建一个带有编号的文本视图。请注意,我们使用 `index + 1` 而不是 `index`,因为数组的索引是从 0 开始的,但我们通常希望编号是从 1 开始的。

## 使用 enumerated 编号

 `enumerated()` 

以下是一个例子:

```swift
struct ContentView: View {
var bookmarks: [BookmarkModel]

var body: some View {
List {
ForEach(Array(bookmarks.enumerated()), id: \.element.id) { index, bookmark in
Text("\(index). \(bookmark.name)")
}
}
}
}
```

我们使用 `Array(bookmarks.enumerated())` 来创建一个元组数组,每个元组包含一个索引和一个 `BookmarkModel` 对象。然后,我们使用 `ForEach` 遍历这个元组数组,并为每个元组创建一个 `Text` 视图。`index` 参数是当前遍历的索引,`bookmark` 参数是当前遍历的 `BookmarkModel` 对象。

## 使用 zip 编号

`zip(_:_:)` 函数可以将两个序列合并为一个元组序列。你可以使用这个函数和 `ForEach` 结构体来为数组中的每个元素添加一个编号。

例如,如果你有一个 `BookmarkModel` 的数组,并且你想为每个书签创建一个文本视图,并在前面添加一个编号,你可以这样做:

```swift
struct ContentView: View {
var bookmarks: [BookmarkModel]

var body: some View {
List {
ForEach(Array(zip(1..., bookmarks)), id: \.1.id) { index, bookmark in
Text("\(index). \(bookmark.name)")
}
}
}
}
```

写出扩展,方便调用

```swift
@dynamicMemberLookup
struct Numbered<Element> {
var number: Int
var element: Element

subscript<T>(dynamicMember keyPath: WritableKeyPath<Element, T>) -> T {
get { element[keyPath: keyPath] }
set { element[keyPath: keyPath] = newValue }
}
}

extension Sequence {
func numbered(startingAt start: Int = 1) -> [Numbered<Element>] {
zip(start..., self)
.map { Numbered(number: $0.0, element: $0.1) }
}
}

extension Numbered: Identifiable where Element: Identifiable {
var id: Element.ID { element.id }
}
```

使用:

```swift
ForEach(bookmark.numbered()) { numberedBookmark in
Text("\(numberedBookmark.number). \(numberedBookmark.name)")
}
```
Loading

0 comments on commit 4bc82f4

Please sign in to comment.