diff --git a/SwiftPamphletApp.xcodeproj/project.pbxproj b/SwiftPamphletApp.xcodeproj/project.pbxproj index 1fd78c3eb..c9b901dde 100644 --- a/SwiftPamphletApp.xcodeproj/project.pbxproj +++ b/SwiftPamphletApp.xcodeproj/project.pbxproj @@ -208,6 +208,12 @@ 0871C6192BA040E5000B620D /* InfoRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0871C6182BA040E5000B620D /* InfoRowView.swift */; }; 0871C61B2BA04D23000B620D /* CategoryRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0871C61A2BA04D23000B620D /* CategoryRowView.swift */; }; 0871C61D2BA05F44000B620D /* InfosView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0871C61C2BA05F44000B620D /* InfosView.swift */; }; + 087ECDFE2BFCC3560011F679 /* Full Screen Modal View(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 087ECDFD2BFCC3560011F679 /* Full Screen Modal View(ap).md */; }; + 087ECE002BFCC36F0011F679 /* confirmationDialog()(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 087ECDFF2BFCC36F0011F679 /* confirmationDialog()(ap).md */; }; + 087ECE022BFCC3980011F679 /* Alert(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 087ECE012BFCC3980011F679 /* Alert(ap).md */; }; + 087ECE042BFCC3AA0011F679 /* Popover(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 087ECE032BFCC3AA0011F679 /* Popover(ap).md */; }; + 087ECE062BFCC3BD0011F679 /* Menu和ContextMenu(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 087ECE052BFCC3BD0011F679 /* Menu和ContextMenu(ap).md */; }; + 087ECE082BFCC3D90011F679 /* HUD(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 087ECE072BFCC3D90011F679 /* HUD(ap).md */; }; 0887A59A2BA28F6D00131359 /* CSGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0887A5992BA28F6D00131359 /* CSGuideView.swift */; }; 0896FB9227BA486900676B7F /* Button(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 0896FB9127BA486900676B7F /* Button(ap).md */; }; 08A4FDC227B25A140068E5BC /* @dynamicMemberLookup动态成员查询(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 08A4FDC127B25A140068E5BC /* @dynamicMemberLookup动态成员查询(ap).md */; }; @@ -495,6 +501,12 @@ 0871C6182BA040E5000B620D /* InfoRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoRowView.swift; sourceTree = ""; }; 0871C61A2BA04D23000B620D /* CategoryRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryRowView.swift; sourceTree = ""; }; 0871C61C2BA05F44000B620D /* InfosView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfosView.swift; sourceTree = ""; }; + 087ECDFD2BFCC3560011F679 /* Full Screen Modal View(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Full Screen Modal View(ap).md"; sourceTree = ""; }; + 087ECDFF2BFCC36F0011F679 /* confirmationDialog()(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "confirmationDialog()(ap).md"; sourceTree = ""; }; + 087ECE012BFCC3980011F679 /* Alert(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Alert(ap).md"; sourceTree = ""; }; + 087ECE032BFCC3AA0011F679 /* Popover(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Popover(ap).md"; sourceTree = ""; }; + 087ECE052BFCC3BD0011F679 /* Menu和ContextMenu(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Menu和ContextMenu(ap).md"; sourceTree = ""; }; + 087ECE072BFCC3D90011F679 /* HUD(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "HUD(ap).md"; sourceTree = ""; }; 0887A5992BA28F6D00131359 /* CSGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSGuideView.swift; sourceTree = ""; }; 0896FB9127BA486900676B7F /* Button(ap).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "Button(ap).md"; sourceTree = ""; }; 08A4FDC127B25A140068E5BC /* @dynamicMemberLookup动态成员查询(ap).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "@dynamicMemberLookup动态成员查询(ap).md"; sourceTree = ""; }; @@ -660,6 +672,10 @@ 085F069E2BF739DC0090310F /* 浮层组件 */, 085F069F2BF73A020090310F /* 视图组件 */, 08522BE727CF625B005FF059 /* 视觉 */, + 087ECE092BFDDAD90011F679 /* 修饰符 */, + 087ECE0A2BFDDB2B0011F679 /* 样式 */, + 087ECE0B2BFDDB320011F679 /* ViewBuilder */, + 087ECE0C2BFDDB3E0011F679 /* 视图协议 */, 08659BC62BE8FD84009B7C00 /* SwiftUI数据流(ap).md */, ); path = SwiftUI; @@ -1013,6 +1029,12 @@ children = ( 08BE638E27CE157D002BC6A8 /* 浮层(ap).md */, 085F06A02BF73AB80090310F /* Sheet(ap).md */, + 087ECDFD2BFCC3560011F679 /* Full Screen Modal View(ap).md */, + 087ECDFF2BFCC36F0011F679 /* confirmationDialog()(ap).md */, + 087ECE012BFCC3980011F679 /* Alert(ap).md */, + 087ECE032BFCC3AA0011F679 /* Popover(ap).md */, + 087ECE052BFCC3BD0011F679 /* Menu和ContextMenu(ap).md */, + 087ECE072BFCC3D90011F679 /* HUD(ap).md */, ); path = "浮层组件"; sourceTree = ""; @@ -1196,6 +1218,34 @@ path = "Picker选择器"; sourceTree = ""; }; + 087ECE092BFDDAD90011F679 /* 修饰符 */ = { + isa = PBXGroup; + children = ( + ); + path = "修饰符"; + sourceTree = ""; + }; + 087ECE0A2BFDDB2B0011F679 /* 样式 */ = { + isa = PBXGroup; + children = ( + ); + path = "样式"; + sourceTree = ""; + }; + 087ECE0B2BFDDB320011F679 /* ViewBuilder */ = { + isa = PBXGroup; + children = ( + ); + path = ViewBuilder; + sourceTree = ""; + }; + 087ECE0C2BFDDB3E0011F679 /* 视图协议 */ = { + isa = PBXGroup; + children = ( + ); + path = "视图协议"; + sourceTree = ""; + }; 0887A5982BA28F3600131359 /* Guide */ = { isa = PBXGroup; children = ( @@ -1670,11 +1720,13 @@ 08D4EBD32BF437510031EDC5 /* Navigation(ap).md in Resources */, 08448FDE279EC74200B61353 /* 比较运算符(ap).md in Resources */, 0850AC062BF2E5DA009FDBBF /* List-移动元素(ap).md in Resources */, + 087ECE022BFCC3980011F679 /* Alert(ap).md in Resources */, 086BEF092BF6C3B100025307 /* DatePicker(ap).md in Resources */, 08659BCD2BE9A40A009B7C00 /* 创建@Model模型(ap).md in Resources */, 08449000279ECAE100B61353 /* flatMap(ap).md in Resources */, 086BEF002BF659E500025307 /* alignmentGuide(ap).md in Resources */, 08D8EFFE2BEFA3E300AA0020 /* 支持多个小组件(ap).md in Resources */, + 087ECE042BFCC3AA0011F679 /* Popover(ap).md in Resources */, 086923382BF19AB7006779A3 /* scrollTargetBehavior分页滚动(ap).md in Resources */, 08448FAB279EC2B400B61353 /* 枚举(ap).md in Resources */, 08522BD627CF3218005FF059 /* Picker(ap).md in Resources */, @@ -1690,6 +1742,7 @@ 08449008279ECB6500B61353 /* zip(ap).md in Resources */, 08448F83279EB78A00B61353 /* 版本兼容(ap).md in Resources */, 08659BD32BE9A478009B7C00 /* SwiftData-检索(ap).md in Resources */, + 087ECE062BFCC3BD0011F679 /* Menu和ContextMenu(ap).md in Resources */, 08448F81279EB75800B61353 /* 系统判断(ap).md in Resources */, 08448FF6279EC9E800B61353 /* Just(ap).md in Resources */, 08026C452869B25700792EF1 /* Distributed Actors(ap).md in Resources */, @@ -1711,7 +1764,9 @@ 0850AC0C2BF2FA2F009FDBBF /* List-轻扫操作(ap).md in Resources */, 08448FFA279ECA5300B61353 /* Empty(ap).md in Resources */, 08026C462869B26000792EF1 /* Swift-DocC(ap).md in Resources */, + 087ECE002BFCC36F0011F679 /* confirmationDialog()(ap).md in Resources */, 08D4EBE42BF4AB9A0031EDC5 /* 布局-留白(ap).md in Resources */, + 087ECDFE2BFCC3560011F679 /* Full Screen Modal View(ap).md in Resources */, 08448FF8279ECA2000B61353 /* PassthroughSubject(ap).md in Resources */, 08D8EFF82BEF912700AA0020 /* 小组件动画(ap).md in Resources */, 08D8F0042BEFA86C00AA0020 /* 小组件-参考资料(ap).md in Resources */, @@ -1769,6 +1824,7 @@ 0850AC102BF30058009FDBBF /* List-完全可点击的行(ap).md in Resources */, 0858C5C72BEBD230004F4C04 /* ContentUnavailableView(ap).md in Resources */, 086BEF0F2BF6C43800025307 /* WheelPicker(ap).md in Resources */, + 087ECE082BFCC3D90011F679 /* HUD(ap).md in Resources */, 08448FD5279EC62700B61353 /* Sets(ap).md in Resources */, 086923342BF178D9006779A3 /* 固定到滚动视图的顶部(ap).md in Resources */, 08448F75279EB62B00B61353 /* Scanner(ap).md in Resources */, diff --git a/SwiftPamphletApp/Guide/View/GuideListView.swift b/SwiftPamphletApp/Guide/View/GuideListView.swift index bcc28edd1..88d474bab 100644 --- a/SwiftPamphletApp/Guide/View/GuideListView.swift +++ b/SwiftPamphletApp/Guide/View/GuideListView.swift @@ -347,7 +347,12 @@ final class GuideListModel { ]), L(t: "浮层组件", sub: [ L(t: "浮层"), - L(t: "Sheet"), + L(t: "Full Screen Modal View"), + L(t: "confirmationDialog()"), + L(t: "Alert"), + L(t: "Popover"), + L(t: "Menu和ContextMenu", icon: "filemenu.and.selection"), + L(t: "HUD"), ]), L(t: "视图组件",sub: [ L(t: "Button"), @@ -363,6 +368,18 @@ final class GuideListModel { L(t: "SwiftUI Canvas"), L(t: "SF Symbol"), L(t: "SwiftCharts"), + ]), + L(t: "修饰符", sub: [ + + ]), + L(t: "样式", sub: [ + + ]), + L(t: "ViewBuilder", sub: [ + + ]), + L(t: "视图协议", sub: [ + ]), L(t: "SwiftUI数据流") ]), diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Alert(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Alert(ap).md" new file mode 100644 index 000000000..7c0b80a31 --- /dev/null +++ "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Alert(ap).md" @@ -0,0 +1,27 @@ + +`.alert` 是一个用于显示警告对话框的修饰符。以下是一个使用 `.alert` 的例子 + +```swift +struct ContentView: View { + @State private var showAlert = false + @State private var authCode = "" + + var body: some View { + Button("删除漫画") { + showAlert = true + } + .alert("确定要删除这部漫画吗?", isPresented: $showAlert) { + Button("删除", role: .destructive) { + print("漫画已删除") + } + Button("取消", role: .cancel) { + print("取消删除") + } + TextField("enter", text: $authCode) + } message: { + Text("请输入验证码?") + } + } +} +``` + diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Full Screen Modal View(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Full Screen Modal View(ap).md" new file mode 100644 index 000000000..9bb8ac730 --- /dev/null +++ "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Full Screen Modal View(ap).md" @@ -0,0 +1,43 @@ + +`fullScreenCover` 是一个非常有用的修饰符,它可以让你全屏显示一个视图。以下是一个使用 `fullScreenCover` 的例子 + +```swift +import SwiftUI + +struct ContentView: View { + @State private var isPresented = false + + var body: some View { + Button(action: { + isPresented = true + }) { + Text("显示电影详情") + } + .fullScreenCover(isPresented: $isPresented) { + MovieDetailView() + } + } +} + +struct MovieDetailView: View { + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + Text("电影标题") + .font(.largeTitle) + Text("电影详情") + .font(.body) + Button(action: { + // Dismiss the view + dismiss() + }) { + Text("关闭") + } + } + } +} +``` + +在这个例子中,我们创建了一个 `ContentView` 视图,其中包含一个按钮。当用户点击这个按钮时,我们将 `isPresented` 设置为 `true`,这将触发 `fullScreenCover` 并显示 `MovieDetailView` 视图。在 `MovieDetailView` 视图中,我们显示电影的标题和详情,以及一个可以关闭视图的按钮。 + diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/HUD(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/HUD(ap).md" new file mode 100644 index 000000000..488a2f7ea --- /dev/null +++ "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/HUD(ap).md" @@ -0,0 +1,40 @@ + +HUD 是 Heads-Up Display 的缩写,翻译为中文是"平视显示器"。在编程中,HUD 通常指的是一种特殊的视图或窗口,它会浮动在应用程序的主界面之上,用于显示某些重要的、临时的信息,比如加载状态、提示信息等。 + +在 SwiftUI 中,创建一个自定义的 HUD 可以通过创建一个新的 View 来实现。以下是一个简单的 HUD 示例: + +```swift +struct HUDView: View { + var body: some View { + ZStack { + VStack { + ProgressView() + Text("加载中...") + .padding(.top, 20) + } + .foregroundColor(.white) + .frame(width: 120, height: 120) + .background(Color.indigo) + .cornerRadius(16) + } + } +} + +struct ContentView: View { + @State private var showHUD = false + + var body: some View { + VStack { + Button("显示 HUD") { + showHUD = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + showHUD = false + } + } + } + .overlay(showHUD ? HUDView() : nil) + } +} +``` + +在这个例子中,我们创建了一个 `HUDView` 视图,它包含一个 `ProgressView` 和一个 `Text`。我们还创建了一个 `ContentView` 视图,其中包含一个按钮,当点击按钮时,会显示 `HUDView`。通过 `DispatchQueue.main.asyncAfter` 方法,我们在 2 秒后隐藏 `HUDView`。 \ No newline at end of file diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Menu\345\222\214ContextMenu(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Menu\345\222\214ContextMenu(ap).md" new file mode 100644 index 000000000..c69fe4623 --- /dev/null +++ "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Menu\345\222\214ContextMenu(ap).md" @@ -0,0 +1,78 @@ + +## Menu 视图 + +`Menu` 视图可以用来创建一个菜单,比如创建相册或文件夹时的下拉选项。用户可以从中选择一个选项。以下是一个使用 `Menu` 的例子,其中包含一个子菜单: + +```swift +struct ContentView: View { + @State private var selectedComic = "漫画1" + + var body: some View { + VStack { + Text("当前选择的漫画是:\(selectedComic)") + .padding() + + Menu { + Button(action: { selectedComic = "漫画1" }) { + Text("漫画1") + } + + Button(action: { selectedComic = "漫画2" }) { + Text("漫画2") + } + + Menu { + Button(action: { selectedComic = "漫画3" }) { + Text("漫画3") + } + + Button(action: { selectedComic = "漫画4" }) { + Text("漫画4") + } + } label: { + Label("更多", systemImage: "folder.circle") + } + } label: { + Label("选择漫画", systemImage: "ellipsis.circle") + } + .menuIndicator(.hidden) + .menuOrder(.fixed) // 保持菜单列表顺序 + + } + } +} +``` + +## `.contextMenu` + +`Menu` 视图可以用来创建一个菜单,用户可以从中选择一个选项。以下是一个使用 `Menu` 的例子: + +```swift +struct Comic: Identifiable,Hashable { + let id = UUID() + let name: String +} + +struct ContentView: View { + let comics = [Comic(name: "漫画1"), Comic(name: "漫画2"), Comic(name: "漫画3")] + + var body: some View { + Table(comics) { + TableColumn("漫画名称", value: \.name) + } + .contextMenu(forSelectionType: Comic.ID.self) { comics in + ControlGroup { + Button("添加到收藏") { } + Button("分享") { } + } + + Button(role: .destructive) { + // 删除动作 + } label: { + Label("删除", systemImage: "trash") + } + } + } +} +``` + diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Popover(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Popover(ap).md" new file mode 100644 index 000000000..86dd0ea87 --- /dev/null +++ "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/Popover(ap).md" @@ -0,0 +1,44 @@ + + +`.popover` 是一个用于显示弹出视图的修饰符。以下是一个使用 `.popover` 的例子 + +```swift +import SwiftUI + +struct ContentView: View { + @State private var showPopover = false + + var body: some View { + Button("显示漫画详情") { + showPopover = true + } + .popover(isPresented: $showPopover, + attachmentAnchor: .point(.bottom), + arrowEdge: .bottom + ) { + VStack { + Text("漫画标题") + .font(.title) + Text("漫画详情") + .font(.body) + Button("关闭") { + showPopover = false + } + .padding() + } + .padding() + .presentationCompactAdaptation(.popover) + } + } +} +``` + +代码中 `PresentationAdaptation` 的可选值: + +- `automatic`:默认适配方式 +- `none`:不进行尺寸等级适应 +- `popover`:优先使用弹出视图 +- `sheet`:优先使用工作表,适用于调整大小类(如 iPhone 默认) +- `fullScreenCover`:优先使用全屏覆盖视图 + + diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/confirmationDialog()(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/confirmationDialog()(ap).md" new file mode 100644 index 000000000..82827015a --- /dev/null +++ "b/SwiftPamphletApp/Resource/Guide/SwiftUI/\346\265\256\345\261\202\347\273\204\344\273\266/confirmationDialog()(ap).md" @@ -0,0 +1,34 @@ + +在 SwiftUI 中,`confirmationDialog()` 是一个用于显示确认对话框的修饰符。支持 iOS 15 或更高版本,也适用于 macOS。以下是一个使用 `confirmationDialog()` 的例子 + +```swift +import SwiftUI + +struct ContentView: View { + @State private var showingConfirmation = false + + var body: some View { + Button("删除漫画") { + showingConfirmation = true + } + .confirmationDialog("确定要删除这部漫画吗?", + isPresented: $showingConfirmation, + titleVisibility: .hidden + ) { + Button("删除", role: .destructive) { + print("漫画已删除") + } + .keyboardShortcut(.defaultAction) // 确保始终会在顶部 + Button("取消", role: .cancel) { + print("取消删除") + } + } message: { + Text("删除操作无法恢复,你确定继续删除么?") + } + } +} +``` + +使用 `.dialogSuppressionToggle(isSuppressed: $isSuppressed)` 能够在 macOS 上添加一个复选框,允许用户选择不再显示确认对话框。 + +