iOS 界面字体回退系统原生 + iPad 适配 + 登录列宽
- 移除自定义 UI 字体,界面统一系统原生:删 BrandFonts.swift(Orbit/Maple woff2 运行期 CTFontManager 注册 + UIKit 外观代理)+ AppDelegate 的 register 调用 + CDropApp 全局 .environment(\.font, .oBody);9 视图 .font(.o语义) → .font(.语义) 系统语义(日志 LogView 消息行本就 .caption.monospaced() 原生等宽);删 Sources/Fonts/(Orbit/Maple woff2 ~20MB)。仅留品牌字标 Fredoka(logo Wordmark),Justfile ios-fonts 改 Fredoka-only、.gitignore 收窄、新增 Shared/Fonts/README.md - iPad 适配:RootView TabView 加 .tabViewStyle(.sidebarAdaptable)(iPad 常规宽度侧边栏 / iPhone 底部标签栏)+ ConversationView 消息线程 maxWidth 680 居中;project.yml 补方向键 UISupportedInterfaceOrientations(iPhone 竖 + 横)/ ~ipad(全 4 向),消除 iPad 多任务方向构建告警 - 登录页内容约束 maxWidth 420 居中,避免 iPad 宽屏主登录按钮 / 二维码区横向拉满(iPhone 不受影响) - 文档:ios/PLAN.md §11.2 改系统原生字体 + 新增 §13 iPad;ios/PARITY.md 字体分叉条目回退 + 导航条目加 iPad + 2026-07-07 漂移行 - 已装 iPhone 15 Pro C + iPad Pro 13" M4 双端、用户确认
This commit is contained in:
@@ -10,11 +10,9 @@ DerivedData/
|
||||
# 原生数据面引擎(曾是 gomobile/pion → xcframework;现已改 libwebrtc SPM,本目录废弃但留忽略防残留)。
|
||||
Frameworks/
|
||||
|
||||
# 品牌字体不入库:Orbit Gothic 为专有 / 定制字体(公开仓库不应分发);Maple Mono / Fredoka 虽开源,
|
||||
# 也统一不入库、改由 `just ios-fonts` 从 CDN / Google Fonts 拉取(镜像 web 的 CDN 思路),保持仓库精简。
|
||||
# 见 Sources/Fonts/README.md。构建前 `just ios-fonts`(已 chain 进 ios-device / ios-sim-build)会自动备齐。
|
||||
Sources/Fonts/*.woff2
|
||||
Sources/Fonts/*.ttf
|
||||
# 品牌字标字体 Fredoka(logo / Wordmark 用)不入库:虽为开源 OFL,仍统一从 Google Fonts 拉、保持仓库
|
||||
# 精简。见 Shared/Fonts/README.md。构建前 `just ios-fonts`(已 chain 进 ios-device / ios-sim-build)自动备齐。
|
||||
# UI 界面字体已改用系统原生字体,不再打包 Orbit Gothic / Maple Mono。
|
||||
Shared/Fonts/*.ttf
|
||||
Shared/Fonts/*.woff2
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# 品牌字标字体 Fredoka(不入库,构建前拉取)
|
||||
|
||||
iOS App 的 **UI 界面字体已统一用系统原生字体**(`.font(.body)` / `.headline` / … 语义样式随 Dynamic Type
|
||||
缩放;日志等宽处用 `.system(…, design: .monospaced)` 原生等宽字体)。**唯一保留的自定义字体是品牌字标
|
||||
Fredoka**——仅用于 logo 的 Wordmark(「Commilitia Drop」,见 `Shared/Brand.swift`),与 web 品牌资产一致。
|
||||
|
||||
| 角色 | 字体 | 落地 | 来源 |
|
||||
|---|---|---|---|
|
||||
| 字标(Wordmark,仅 logo) | **Fredoka SemiBold** | `Shared/Fonts/Fredoka-SemiBold.ttf` | Google Fonts 变量字体实例化(OFL) |
|
||||
|
||||
## 为什么不提交进仓库
|
||||
|
||||
Fredoka 虽为开源(OFL),仍统一走拉取以保持仓库精简;经 `.gitignore` 排除,本 README 入库作说明。
|
||||
|
||||
## 如何备齐
|
||||
|
||||
```sh
|
||||
just ios-fonts
|
||||
```
|
||||
|
||||
幂等(已存在则跳过)。`just ios-device` / `just ios-sim-build` 构建前已自动 chain 本步,正常无需手动跑。
|
||||
**前置**:`curl`;造 Fredoka 静态实例需 `python3` + `fonttools`(`python3 -m pip install --user fonttools`)。
|
||||
|
||||
## 接入方式(代码)
|
||||
|
||||
Fredoka 是 ttf,经 `UIAppFonts`(各 target 的 Info.plist)注册;`Wordmark`(`Shared/Brand.swift`)用
|
||||
`.font(.custom("Fredoka-SemiBold", size:))`。主 app、Share 扩展、控件扩展均可用(字标体积小,无 CJK)。
|
||||
|
||||
> 历史:早先曾为 UI 引入 Orbit Gothic(Sans)+ Maple Mono(Mono)自定义字体栈,后统一回退系统原生字体
|
||||
> 移除(仅保留 Fredoka 字标)。故不再有 `Sources/Fonts/` 与运行期 `CTFontManagerRegisterFontsForURL` 注册。
|
||||
@@ -10,8 +10,6 @@ final class AppDelegate: NSObject, UIApplicationDelegate
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool
|
||||
{
|
||||
// 品牌字体须在任何 UI 渲染前注册 + 设外观代理(导航 / 标签栏标题换 Orbit Gothic)。
|
||||
BrandFonts.register()
|
||||
// BGContinuedProcessingTask 的 launch handler 须在 launch 完成前注册。
|
||||
BackgroundTaskManager.shared.registerOnce()
|
||||
return true
|
||||
|
||||
@@ -35,7 +35,7 @@ struct LoginView: View
|
||||
if !auth.statusText.isEmpty
|
||||
{
|
||||
Text(auth.statusText)
|
||||
.font(.oCallout)
|
||||
.font(.callout)
|
||||
.foregroundStyle(auth.qrExpired ? Color.red : Color.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
@@ -49,7 +49,7 @@ struct LoginView: View
|
||||
qrArea
|
||||
refreshButton
|
||||
Text(t("ios.login.guide"))
|
||||
.font(.oFootnote)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
@@ -58,7 +58,7 @@ struct LoginView: View
|
||||
label:
|
||||
{
|
||||
Text(t("ios.login.scanAlt"))
|
||||
.font(.oSubheadline)
|
||||
.font(.subheadline)
|
||||
}
|
||||
.padding(.horizontal, 40)
|
||||
.onChange(of: showQR)
|
||||
@@ -68,6 +68,10 @@ struct LoginView: View
|
||||
|
||||
Spacer()
|
||||
}
|
||||
// iPad:登录内容约束到可读列宽并水平居中,避免主登录按钮 / 二维码区在宽屏横向拉满
|
||||
//(iPhone 宽度不足 420 仍照常填满,不受影响)。
|
||||
.frame(maxWidth: 420)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.tint(.cdropAccent)
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
import CoreText
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
// 品牌字体接入(对齐 web 字体栈 --font-sans / --font-mono):
|
||||
// - Sans 正文 = Orbit Gothic CJK SC(Regular 400 + SemiBold 600 两变体)
|
||||
// - Mono = Maple Mono(拉丁 / 符号)
|
||||
// 字体以 woff2 内嵌(体积约 TTF 的 1/3;iOS Core Text 自 iOS 13 起原生支持 woff/woff2),故经
|
||||
// CTFontManagerRegisterFontsForURL 运行期注册(UIAppFonts 历史上只认 ttf/otf,woff2 走运行期最稳)。
|
||||
// 仅主 app 接入——CJK 字体大(~10MB/字重),不进 Share / 控件扩展(它们保持系统字 + 仅 Fredoka 字标)。
|
||||
// 生僻字(超出 Orbit Gothic 字汇)由 iOS 字体级联自动回退系统苹方,不出豆腐块。
|
||||
enum BrandFonts
|
||||
{
|
||||
static let sansRegular = "OrbitGothicCJKsc-Regular"
|
||||
static let sansSemibold = "OrbitGothicCJKsc-SemiBold"
|
||||
static let mono = "MapleMono-Regular"
|
||||
|
||||
// app 启动最早期(AppDelegate didFinishLaunching)调用一次:注册字体 + 设 UIKit 外观代理。
|
||||
static func register()
|
||||
{
|
||||
for name in [ sansRegular, sansSemibold, mono ]
|
||||
{
|
||||
guard let url = Bundle.main.url(forResource: name, withExtension: "woff2") else { continue }
|
||||
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
|
||||
}
|
||||
applyUIKitAppearance()
|
||||
}
|
||||
|
||||
// SwiftUI 的导航标题 / 标签栏标题经 UIKit 渲染,SwiftUI 无 API 直改,故经 appearance 代理统一换 Orbit。
|
||||
private static func applyUIKitAppearance()
|
||||
{
|
||||
if let large = UIFont(name: sansSemibold, size: 32)
|
||||
{
|
||||
UINavigationBar.appearance().largeTitleTextAttributes = [ .font: large ]
|
||||
}
|
||||
if let inline = UIFont(name: sansSemibold, size: 17)
|
||||
{
|
||||
UINavigationBar.appearance().titleTextAttributes = [ .font: inline ]
|
||||
}
|
||||
if let tab = UIFont(name: sansRegular, size: 10)
|
||||
{
|
||||
let item = UITabBarItem.appearance()
|
||||
item.setTitleTextAttributes([ .font: tab ], for: .normal)
|
||||
item.setTitleTextAttributes([ .font: tab ], for: .selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 字体助手:Orbit Gothic(Sans)+ Maple Mono(Mono)。relativeTo 让自定义字体随动态字号缩放
|
||||
// (不丢可访问性)。weight 仅二档——bold 走 SemiBold 变体,其余走 Regular(再粗由系统合成)。
|
||||
extension Font
|
||||
{
|
||||
static func orbit(_ size: CGFloat, bold: Bool = false, relativeTo style: Font.TextStyle = .body) -> Font
|
||||
{
|
||||
.custom(bold ? BrandFonts.sansSemibold : BrandFonts.sansRegular, size: size, relativeTo: style)
|
||||
}
|
||||
|
||||
static func maple(_ size: CGFloat, relativeTo style: Font.TextStyle = .body) -> Font
|
||||
{
|
||||
.custom(BrandFonts.mono, size: size, relativeTo: style)
|
||||
}
|
||||
|
||||
// 与 SF 语义文本样式对位的 Orbit 版本(字号 / 字重对齐 SF 默认值),供全局替换 .font(.xxx)。
|
||||
// headline / title* 默认偏粗 → SemiBold;正文 / 说明类 → Regular。
|
||||
static let oLargeTitle = orbit(34, bold: true, relativeTo: .largeTitle)
|
||||
static let oTitle = orbit(28, bold: true, relativeTo: .title)
|
||||
static let oTitle2 = orbit(22, bold: true, relativeTo: .title2)
|
||||
static let oTitle3 = orbit(20, bold: true, relativeTo: .title3)
|
||||
static let oHeadline = orbit(17, bold: true, relativeTo: .headline)
|
||||
static let oBody = orbit(17, relativeTo: .body)
|
||||
static let oCallout = orbit(16, relativeTo: .callout)
|
||||
static let oSubheadline = orbit(15, relativeTo: .subheadline)
|
||||
static let oFootnote = orbit(13, relativeTo: .footnote)
|
||||
static let oCaption = orbit(12, relativeTo: .caption)
|
||||
static let oCaption2 = orbit(11, relativeTo: .caption2)
|
||||
|
||||
// Mono:等宽场景(日志行 / 设备 id / 字节计数)。
|
||||
static let mCaption = maple(12, relativeTo: .caption)
|
||||
static let mFootnote = maple(13, relativeTo: .footnote)
|
||||
static let mBody = maple(15, relativeTo: .body)
|
||||
}
|
||||
@@ -15,9 +15,6 @@ struct CDropApp: App
|
||||
WindowGroup
|
||||
{
|
||||
AppRoot(auth: auth, engine: engine)
|
||||
// 全局默认字体=Orbit Gothic 正文(无显式 .font 的文本随之走品牌 Sans)。显式语义样式
|
||||
// 由各视图改用 .font(.oXxx)(见 BrandFonts)覆盖到品牌字。
|
||||
.environment(\.font, .oBody)
|
||||
}
|
||||
.onChange(of: scenePhase)
|
||||
{ _, phase in
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
# 品牌字体(不入库,构建前拉取)
|
||||
|
||||
iOS App 的字体栈对齐 web(见 `web/src/styles/fonts.css`):
|
||||
|
||||
| 角色 | 字体 | 落地 | 来源 |
|
||||
|---|---|---|---|
|
||||
| Sans(正文 / 界面) | **Orbit Gothic CJK SC**(Regular 400 + SemiBold 600) | `Sources/Fonts/OrbitGothicCJKsc-*.woff2` | 私有 CDN `orbit-gothic.cdn.commilitia.net` |
|
||||
| Mono(等宽) | **Maple Mono**(Regular) | `Sources/Fonts/MapleMono-Regular.woff2` | 私有 CDN `maple-mono.cdn.commilitia.net`(OFL) |
|
||||
| 字标(Wordmark) | **Fredoka SemiBold** | `Shared/Fonts/Fredoka-SemiBold.ttf` | Google Fonts 变量字体实例化(OFL) |
|
||||
|
||||
## 为什么不提交进仓库
|
||||
|
||||
- **Orbit Gothic 是专有 / 定制字体**——公开仓库(见根 README 的双 remote 发布)不应分发它。web 端同样不入库、
|
||||
改从私有 CDN 拉。
|
||||
- Maple Mono / Fredoka 虽开源(OFL),也统一走拉取,保持仓库精简(Orbit 两变体 woff2 合计 ~20MB)。
|
||||
|
||||
字体文件经 `.gitignore` 排除(`Sources/Fonts/*.woff2,*.ttf`、`Shared/Fonts/*`);本 README 入库作说明。
|
||||
|
||||
## 如何备齐
|
||||
|
||||
```sh
|
||||
just ios-fonts
|
||||
```
|
||||
|
||||
幂等(已存在则跳过)。`just ios-device` / `just ios-sim-build` 构建前已自动 chain 本步,正常无需手动跑。
|
||||
|
||||
**前置**:`curl`;造 Fredoka 静态实例需 `python3` + `fonttools`(`python3 -m pip install --user fonttools`)。
|
||||
CDN 不可达时构建会失败——离线则手动把上述文件放到对应目录即可。
|
||||
|
||||
## 接入方式(代码)
|
||||
|
||||
字体以 **woff2** 内嵌(体积约 TTF 的 1/3;iOS 13+ Core Text 原生支持 woff/woff2)。`UIAppFonts`(Info.plist)
|
||||
历史上只认 ttf/otf,故 woff2 走**运行期注册** `CTFontManagerRegisterFontsForURL`——见 `Sources/BrandFonts.swift`
|
||||
的 `BrandFonts.register()`(`AppDelegate` 启动调用)。Fredoka 字标是 ttf,经 `UIAppFonts` 注册。
|
||||
|
||||
仅**主 app** 接入 Orbit / Maple(CJK 字体大,不进 Share / 控件扩展——它们保持系统字 + 仅 Fredoka 字标)。
|
||||
生僻字超出 Orbit Gothic 字汇时由 iOS 字体级联自动回退系统苹方,不出豆腐块。
|
||||
|
||||
字体助手见 `Sources/BrandFonts.swift`:`Font.oBody/.oHeadline/...`(Orbit,对位 SF 语义样式)、`Font.maple(...)`
|
||||
(Maple Mono);导航 / 标签栏标题经 UIKit appearance 代理换 Orbit。
|
||||
@@ -70,5 +70,18 @@
|
||||
<true/>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -109,10 +109,10 @@ struct LogView: View
|
||||
HStack(spacing: 6)
|
||||
{
|
||||
Image(systemName: levelSymbol(entry.level))
|
||||
.font(.oCaption)
|
||||
.font(.caption)
|
||||
.foregroundStyle(levelTint(entry.level))
|
||||
Text(timeString(entry.at))
|
||||
.font(.oCaption2)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.monospacedDigit()
|
||||
}
|
||||
|
||||
@@ -69,16 +69,16 @@ struct MessagesView: View
|
||||
HStack(spacing: 12)
|
||||
{
|
||||
Image(systemName: deviceSymbol(convo.deviceType))
|
||||
.font(.oTitle3)
|
||||
.font(.title3)
|
||||
.foregroundStyle(convo.online ? Color.green : Color.secondary)
|
||||
.frame(width: 28)
|
||||
VStack(alignment: .leading, spacing: 3)
|
||||
{
|
||||
Text(convo.peerName)
|
||||
.font(.oBody)
|
||||
.font(.body)
|
||||
.lineLimit(1)
|
||||
Text(previewText(convo))
|
||||
.font(.oCaption)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
@@ -88,7 +88,7 @@ struct MessagesView: View
|
||||
if convo.lastAt > 0
|
||||
{
|
||||
Text(relativeTime(convo.lastAt))
|
||||
.font(.oCaption2)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
// 该设备未读红点(社交软件式逐设备计数);消息 tab 徽标=各设备未读总和(engine.unreadMessages)。
|
||||
@@ -96,7 +96,7 @@ struct MessagesView: View
|
||||
if unread > 0
|
||||
{
|
||||
Text("\(unread)")
|
||||
.font(.oCaption2)
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 7)
|
||||
@@ -159,6 +159,9 @@ struct ConversationView: View
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
// iPad:把气泡列约束到可读宽度并居中,避免在宽内容区里横向拉满(呼应 iMessage)。
|
||||
.frame(maxWidth: 680)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.onChange(of: thread.count) { scrollToLast(proxy) }
|
||||
.onAppear { scrollToLast(proxy) }
|
||||
@@ -213,7 +216,7 @@ struct ConversationView: View
|
||||
.submitLabel(.send)
|
||||
.onSubmit { send() }
|
||||
Button { send() }
|
||||
label: { Image(systemName: "paperplane.fill").font(.oTitle3) }
|
||||
label: { Image(systemName: "paperplane.fill").font(.title3) }
|
||||
.disabled(draft.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
||||
}
|
||||
.padding()
|
||||
@@ -222,7 +225,7 @@ struct ConversationView: View
|
||||
else
|
||||
{
|
||||
Text(t("ios.messages.viewOnly"))
|
||||
.font(.oCaption)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
|
||||
@@ -35,10 +35,10 @@ private struct NoticeBanner: View
|
||||
HStack(spacing: 10)
|
||||
{
|
||||
Image(systemName: symbol)
|
||||
.font(.oHeadline)
|
||||
.font(.headline)
|
||||
.foregroundStyle(tint)
|
||||
Text(notice.text)
|
||||
.font(.oSubheadline)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.primary)
|
||||
.lineLimit(2)
|
||||
Spacer(minLength: 8)
|
||||
|
||||
@@ -60,6 +60,10 @@ struct RootView: View
|
||||
}
|
||||
}
|
||||
}
|
||||
// iPad 适配:常规宽度(iPad)自适应为侧边栏(Files / Music 式,左侧标签栏 + 右侧内容区),
|
||||
// 紧凑宽度(iPhone)仍底部标签栏——同一 TabView 经 sidebarAdaptable 双形态,无需为 iPad 另写
|
||||
// 导航壳。各标签页的 NavigationStack 在右侧内容区内推详情。见 ios/PLAN.md §13。
|
||||
.tabViewStyle(.sidebarAdaptable)
|
||||
.tabBarMinimizeBehavior(.onScrollDown)
|
||||
.tint(.cdropAccent)
|
||||
// 瞬时通知横幅覆盖在内容顶部(#2)。
|
||||
@@ -162,7 +166,7 @@ struct TransferListView: View
|
||||
label:
|
||||
{
|
||||
Image(systemName: "paperplane.fill")
|
||||
.font(.oTitle2)
|
||||
.font(.title2)
|
||||
.padding(18)
|
||||
}
|
||||
.buttonStyle(.glassProminent)
|
||||
@@ -220,7 +224,7 @@ struct TransferListView: View
|
||||
HStack
|
||||
{
|
||||
Text(title)
|
||||
.font(.oSubheadline)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
@@ -342,7 +346,7 @@ struct TransferCardView: View
|
||||
? "arrow.down.circle" : "arrow.up.circle")
|
||||
.foregroundStyle(.secondary)
|
||||
Text(item.fileName)
|
||||
.font(.oHeadline)
|
||||
.font(.headline)
|
||||
.lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
@@ -351,7 +355,7 @@ struct TransferCardView: View
|
||||
ProgressView(value: progressOf(item))
|
||||
}
|
||||
Text(detailLine)
|
||||
.font(.oCaption)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding()
|
||||
@@ -625,14 +629,14 @@ struct DeviceListView: View
|
||||
if isSelf(dev)
|
||||
{
|
||||
Text(t("ios.devices.thisDevice"))
|
||||
.font(.oCaption2)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(Color.cdropAccent)
|
||||
}
|
||||
}
|
||||
Text(dev.online
|
||||
? t("settings.online")
|
||||
: t("settings.lastSeen", [ "time": relativeTime(dev.lastSeen) ]))
|
||||
.font(.oCaption)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
@@ -664,7 +668,7 @@ struct SettingsView: View
|
||||
if !appVersion.isEmpty
|
||||
{
|
||||
Text(appVersion)
|
||||
.font(.oCaption2)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
@@ -718,7 +722,7 @@ struct SettingsView: View
|
||||
if !engine.lastEngineLog.isEmpty
|
||||
{
|
||||
Text(engine.lastEngineLog)
|
||||
.font(.oCaption2)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
@@ -861,7 +865,7 @@ struct ReceivedFilesView: View
|
||||
Text(url.lastPathComponent)
|
||||
.lineLimit(1)
|
||||
Text(fileSizeString(url))
|
||||
.font(.oCaption)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,14 +59,14 @@ struct ScanSheet: View
|
||||
if invalid
|
||||
{
|
||||
Text(t("ios.scan.invalid"))
|
||||
.font(.oCallout)
|
||||
.font(.callout)
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 14)
|
||||
.background(.red.opacity(0.85), in: Capsule())
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
Text(t("ios.scan.hint"))
|
||||
.font(.oFootnote)
|
||||
.font(.footnote)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 14)
|
||||
|
||||
@@ -40,7 +40,7 @@ struct SendComposeSheet: View
|
||||
.lineLimit(1)
|
||||
Spacer()
|
||||
Text(formatBytes(file.size))
|
||||
.font(.oCaption)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,18 @@ targets:
|
||||
- zh-Hant
|
||||
- en
|
||||
UILaunchScreen: {}
|
||||
# 界面方向:iPhone 竖屏 + 横屏(不含上下颠倒,遵 iPhone 惯例);iPad 支持全部 4 向——通用 App
|
||||
# 若不锁全屏(UIRequiresFullScreen 未设),iPad 多任务(Split View / Slide Over)要求支持全部方向,
|
||||
# 否则构建告警「All interface orientations must be supported...」。
|
||||
UISupportedInterfaceOrientations:
|
||||
- UIInterfaceOrientationPortrait
|
||||
- UIInterfaceOrientationLandscapeLeft
|
||||
- UIInterfaceOrientationLandscapeRight
|
||||
"UISupportedInterfaceOrientations~ipad":
|
||||
- UIInterfaceOrientationPortrait
|
||||
- UIInterfaceOrientationPortraitUpsideDown
|
||||
- UIInterfaceOrientationLandscapeLeft
|
||||
- UIInterfaceOrientationLandscapeRight
|
||||
# 后台续传:BGContinuedProcessingTask 的通配标识符须在此声明,系统才允许注册 / 提交
|
||||
# (见 BackgroundTaskManager)。默认资源(CPU + 网络)无需额外 entitlement。
|
||||
BGTaskSchedulerPermittedIdentifiers:
|
||||
|
||||
+3
-2
@@ -30,8 +30,8 @@
|
||||
### 4. 允许分叉登记(明确 OK 不一致)
|
||||
|
||||
- **视觉材质**:液态玻璃 vs Theme B。
|
||||
- **导航外壳**:原生 `NavigationStack` / `TabView` vs web 布局。
|
||||
- **字体 / 排版**(2026-06-28 更新,字族已收敛):两端现**同用** Orbit Gothic(Sans)+ Maple Mono(Mono)+ Fredoka(字标)——字族升为共享源(强约束③延伸)。**残余分叉**仅在落地机制(iOS woff2 运行期 `CTFontManagerRegisterFontsForURL` 注册 + UIKit appearance 代理 + Dynamic Type vs web CSS `@font-face`)与间距 / 圆角 / 字阶(各用平台母语);生僻字 iOS 回退苹方、web 回退 Noto。Orbit 为专有字体,两端均不入库、构建期从 CDN 拉。
|
||||
- **导航外壳**:原生 `NavigationStack` / `TabView`(iPad 经 `.tabViewStyle(.sidebarAdaptable)` 呈侧边栏、iPhone 底部标签栏)vs web 布局。
|
||||
- **字体 / 排版**(2026-07-07 更新,iOS 撤下自定义字体):**iOS 界面用系统原生字体**(SF 语义样式 + Dynamic Type;日志等宽用原生 monospaced)vs web Orbit Gothic / Noto——字体选择归**允许分叉**(各用平台母语,间距 / 圆角 / 字阶同)。**唯一共享**的自定义字体是**品牌字标 Fredoka**(仅 logo 的 Wordmark,两端一致);品牌 logo(mark + 字标)仍是共享源(强约束③)。(撤下 2026-06-28 曾引入的 Orbit Gothic + Maple Mono UI 字体栈。)
|
||||
- **分享引导界面**(仅 iOS):Share Extension 内的吉祥物引导卡(`ShareGuideView`)引用户回主程序完成发送——平台专属流程,web 无对应。
|
||||
- **平台专属**:控制中心控件、Share Extension(仅 iOS);Web Push 订阅 UI、桌面设置区块(非 iOS)。
|
||||
- **控制中心控件标签**:运行时控件标签(用户点按的那个)经 `t()` 跟随设备语言;但「添加控件」库名 + Shortcuts 标题是 `LocalizedStringResource` 静态值、`CDropWidgets` target 无 String Catalog,故对 en / zh-TW 用户回退简体字面量(审查 L7,平台专属功能可接受分叉;要彻底本地化须为 Widgets target 加 `.xcstrings`)。
|
||||
@@ -62,3 +62,4 @@
|
||||
| 2026-06-24 | engine/main.ts:transfers 推送节流 ~3Hz + toWire 加 ice 摘要(未提交,**待部署**) | EngineController 手动容忍解析 + ICE 字段;RootView 详情页 ICE 行 + 收到文件视图 + 设备名编辑 + 设备计数;DownloadManager listFiles/delete;新 DeviceNameStore / QuickLookPreview;project.yml 加文件共享键 + 品牌化权限串(未提交) | **首轮真机测试反馈修复**:① 速度数字 ~10Hz 闪 → 节流活跃传输推送到 3Hz(iOS-only,亦降 P2P 接收主线程抖动);② 设备列表空 → 原生改手动容忍解析(严格 JSONDecoder 遇 bool/int 失配整组失败的隐患),加「已知设备」计数诊断;③ 收到文件无处开 / Files 不可见 / 阻塞发送 → `UIFileSharingEnabled`+`LSSupportsOpeningDocumentsInPlace`(Documents 已是落地目录)+ 应用内「收到的文件」视图(QuickLook 预览 / 系统分享 / 转发在线设备 / 删除);④ 设备名不可改 → `DeviceNameStore`(UserDefaults)+ 设置页编辑框(后端令牌烤名、改名下次登录生效);⑤ P2P <100KB/s(relay 1MB/s)→ 详情页暴露选中 ICE 候选对诊断是否 TURN 中继(不盲改共享 p2p.ts 水位)。i18n 加 `ios.files.*`/`ios.settings.deviceName|deviceNameNote|deviceCount`,ICE 行复用 `transfer.debug.*`。web 双过 + xcodebuild 编过 + 模拟器设置 / 传输屏截图验。**已部署 prod**:`engine.html` → `engine-BC6mZ9zP.js`(含节流 + ice 摘要) |
|
||||
| 2026-06-27 | source.ts(新 FileSource)+ p2p/relay/transfer 改 src + engine/main.ts 加 registerPush/provisionWidgetSession + i18n 加 `ios.bg.*`/`ios.share.*`/`ios.control.*`(本提交) | 新 BackgroundTaskManager / AppDelegate / PushRegistry / Shared(AppGroup,I18n,CDropAPI,WidgetSession) / Share(ShareViewController) / Widgets(控件+intents+client);EngineController 加 Range handler / 后台 sync / 分享导入 / 控件代铸;project.yml 加两扩展 target + 真机签名 scaffold;包名改 `net.commilitia.Commilitia-Drop`(本提交) | **计划完成(I3–I7)**:发送端流式(FileSource + 原生 Range,新增**分享发送 + 控件**两条非引擎流程,对应残余项 1/2)/ 后台续传 / APNs(后端 internal/apns + 原生注册,新后端字段 `push_subscriptions.platform` + 端点 `POST /api/push/apns/register` 对应残余项 3)/ Share Extension / 控制中心两控件(新允许分叉:控件标签见上)。经 ultracode 多 agent 审查(0 HIGH,修 2 MED + 7 LOW)。web tsc/build + go build/test + xcodebuild 全过;模拟器验登录 / 分享弹窗 / 控件嵌入 / App Group 容器。**真机 + 真发推送 + 分发账号门控**(见 REALDEVICE.md)。**待部署 prod**:engine.html(新桥)+ cdrop binary(APNs 端点 + platform 列) |
|
||||
| 2026-06-28 | `p2p.ts` 翻 `IOS_NATIVE=true` + 新 `p2pIos.ts` + `p2pNative.ts`/`net/ios.ts` 微调;平均速度 `store/slices/transfer.ts` + `TransferRow.tsx`;i18n 加 `transfer.avgRate`(native-transfer C1/C2) | 新 `Engine/LibWebRtcEngine.swift` + 改 `NativeTransfer.swift`/`EngineController.swift`(原生数据面);新 `SendComposeSheet.swift` + 重写 `MessagesView.swift`(发送预览 / 按设备会话 + 每设备未读);新 `Shared/Brand.swift`/`Sources/BrandFonts.swift`/`Share/ShareGuideView.swift` + 品牌资产;i18n 加 `ios.send.*`/`ios.messages.*`/`ios.share.guide.*`(native-transfer C1/C2) | **iOS 原生数据面 libwebrtc**(P2P 数据面下沉原生、线协议逐字节对齐桌面 pion,PLAN 决策 F;编排留无头 WebView)+ **UX 改造**(发送预览直入 / 分享直入 / 消息按设备会话 + 每设备未读红点 / 删除粒度——对应残余项 1/2)+ **品牌 + 字体**(logo + Orbit Gothic / Maple Mono / Fredoka,字族升共享源、字体分叉条目已更新)+ **全平台平均速度**(残余项 2,三端一致)。环回端到端 20MB 逐字节 + 模拟器全 app 编译 + web tsc/build 过;真机已装、用户反馈“传输总体良好”,真网吞吐互通为剩余闸。**待部署 prod**:engine.html(`IOS_NATIVE=true` 引擎);桌面平均速度待 `desktop-build-{mac,win}` 重建分发 |
|
||||
| 2026-07-07 | 无(纯 iOS 端改动,web 字体栈不变) | 移除 Orbit / Maple 自定义 UI 字体(删 `BrandFonts.swift` + `Sources/Fonts/`),9 视图 `.o语义`→系统语义 + 日志原生等宽,仅留 Fredoka 字标;`RootView` 加 `.tabViewStyle(.sidebarAdaptable)` + `ConversationView` 可读宽度(native-transfer,待 commit) | **iOS UI 字体回退系统原生**(撤 2026-06-28 的 Orbit/Maple;字体条目已改回允许分叉、仅 Fredoka 字标共享)+ **iPad 适配**(TabView 侧边栏自适应 + 消息线程可读宽度;`TARGETED_DEVICE_FAMILY` 本就 1,2)。`just ios-sim-build` 编译过(BUILD SUCCEEDED);iPad 观感待肉眼验 |
|
||||
|
||||
+20
-8
@@ -272,22 +272,24 @@ Share Extension 落法(据上):**抓文件 → 写 App Group 容器 →
|
||||
|
||||
---
|
||||
|
||||
## 11. 品牌与字体系统(2026-06-28,对齐 web)
|
||||
## 11. 品牌 logo 与界面字体(2026-06-28 引入 logo;字体 2026-07-07 改系统原生)
|
||||
|
||||
iOS 界面引入与 web 一致的品牌资产与字体栈——这是强约束③(§5,品牌资产 + 主色单一源)在视觉层的延伸。
|
||||
iOS 引入与 web 一致的**品牌资产(吉祥物 mark + Fredoka 字标)**作共享源(强约束③,§5);**界面字体则用系统原生**(见 §11.2,归允许分叉)。
|
||||
|
||||
### 11.1 品牌 logo
|
||||
|
||||
- `Shared/Brand.swift`:`BrandMark`(吉祥物,资产 `Shared/Brand.xcassets/LogoMark`,源 `web/public/logo-mark.png`)、`Wordmark`(“Commilitia” 主色 + “Drop” `cdropAmber`,Fredoka SemiBold)、`BrandLockup`、`BrandHero(tagline:)`。
|
||||
- 接入点:登录页(`BrandMark` + `Wordmark` 取代纯文字标题)、传输空态(`BrandHero`)、设置页顶部横向品牌头、控制中心控件重设计(吉祥物 + 品牌渐变 + 全宽按钮,最大化空间)。`Theme.swift` 加 `Color.cdropAmber`(#DB9410 / #F8C637)。
|
||||
|
||||
### 11.2 字体栈(Orbit Gothic + Maple Mono + Fredoka)
|
||||
### 11.2 界面字体=系统原生(2026-07-07 定案,撤下自定义字体栈)
|
||||
|
||||
- 与 web `fonts.css` 同源:Sans = **Orbit Gothic CJK SC**(Regular + SemiBold)、Mono = **Maple Mono**、字标 = **Fredoka SemiBold**。
|
||||
- **woff2 运行期注册**:Orbit / Maple 以 woff2 内嵌(约 TTF 的 1/3 体积),`UIAppFonts` 历史只认 ttf/otf,故经 `CTFontManagerRegisterFontsForURL` 运行期注册(iOS 13+ Core Text 原生支持 woff2,实测 register→true)——见 `Sources/BrandFonts.swift` 的 `BrandFonts.register()`(`AppDelegate` 启动调)。Fredoka 是 ttf,走 `UIAppFonts`。
|
||||
- **助手 + sweep**:`BrandFonts` 暴露 `Font.oBody/.oHeadline/…`(Orbit,对位 SF 语义样式)、`Font.maple(…)`(Maple Mono);导航 / 标签栏标题经 UIKit appearance 代理换 Orbit;7 个视图的 `.font(.语义)` → `.font(.o语义)` sweep。
|
||||
- **仅主 app**:CJK 字体大,不进 Share / 控件扩展(它们保持系统字 + 仅 Fredoka 字标);生僻字超 Orbit 字汇时 iOS 字体级联自动回退苹方,不出豆腐块。
|
||||
- **字体不入库**:Orbit Gothic 为专有 / 定制字体(公开仓库不分发),Maple / Fredoka 虽开源也统一不入库保持精简;经 `.gitignore` 排除,构建前 `just ios-fonts`(已 chain 进 `ios-device` / `ios-sim-build`)从私有 CDN / Google Fonts 幂等拉取。详见 `Sources/Fonts/README.md`。
|
||||
> **决策更新(2026-07-07)**:移除早先(2026-06-28)引入的 Orbit Gothic(Sans)+ Maple Mono(Mono)自定义 UI 字体栈,**界面统一回退系统原生字体**。**唯一保留的自定义字体是品牌字标 Fredoka**(仅 logo 的 `Wordmark`,见 §11.1)。
|
||||
|
||||
- **系统语义样式**:UI 用 `.font(.body)` / `.headline` / `.caption` / … SF 语义样式(随 Dynamic Type 缩放);日志等宽处用 `.system(…, design: .monospaced)` **原生等宽字体**(`LogView` 消息行)。
|
||||
- **缘由**:CJK 自定义字体体积大(Orbit 两变体 woff2 合计 ~20MB)、须运行期注册,收益不抵成本;系统字体免维护,且 Dynamic Type / 无障碍对比 / 生僻字级联回退最稳。
|
||||
- **落地**:删 `Sources/BrandFonts.swift`(含 woff2 运行期 `CTFontManagerRegisterFontsForURL` 注册 + UIKit 导航 / 标签栏外观代理)+ `AppDelegate` 的 `BrandFonts.register()` 调用 + `CDropApp` 全局 `.environment(\.font, .oBody)`;9 个视图的 `.font(.o语义)` → `.font(.语义)` 回退系统;删 `Sources/Fonts/`(Orbit / Maple woff2)。
|
||||
- **Fredoka 字标(保留)**:`Shared/Fonts/Fredoka-SemiBold.ttf`(ttf,经各 target `UIAppFonts` 注册),`Wordmark` 用 `.font(.custom("Fredoka-SemiBold", size:))`;不入库、`just ios-fonts` 构建前从 Google Fonts 拉。详见 `Shared/Fonts/README.md`。
|
||||
- **对 PARITY 的影响**:字体从「两端同用 Orbit / Maple」回退为「iOS 系统原生 vs web Orbit Gothic」的**允许分叉**;仅品牌 logo(mark + Fredoka 字标)仍是共享源(强约束③)。
|
||||
|
||||
### 11.3 分享引导界面
|
||||
|
||||
@@ -301,6 +303,16 @@ iOS 界面引入与 web 一致的品牌资产与字体栈——这是强约束
|
||||
|
||||
---
|
||||
|
||||
## 13. iPad 适配(2026-07-07)
|
||||
|
||||
iOS 目标本就 `TARGETED_DEVICE_FAMILY = 1,2`(iPhone + iPad 通用),本轮补齐 iPad 的 UI 适配。
|
||||
|
||||
- **导航:`TabView` 经 `.tabViewStyle(.sidebarAdaptable)` 双形态**(`RootView`)——iPad 常规宽度自适应为**侧边栏**(Files / Music 式,左标签栏 + 右内容区),iPhone 紧凑宽度仍**底部标签栏**(浮动液态玻璃)。同一 TabView、同一 5 标签,无需为 iPad 另写导航壳;各标签页的 `NavigationStack` 在右侧内容区内推详情。未读红点 `.badge()` 在侧边栏同样呈现。
|
||||
- **可读宽度**:消息线程(`ConversationView`)气泡列约束 `maxWidth: 680` 并居中,避免在宽内容区横向拉满(呼应 iMessage)。列表类(传输 / 文件 / 设备 / 设置)保留内容区满宽,符合 iPad 原生列表观感(Settings / Mail 式)。sheet(发送预览 / 扫码 / 预览)在 iPad 自动呈 form sheet 居中。
|
||||
- **验证**:`just ios-sim-build`(iphonesimulator,通用二进制)编译过(BUILD SUCCEEDED)。iPad 模拟器 / 真机观感待肉眼验。
|
||||
|
||||
---
|
||||
|
||||
## 附:与桌面端策略对照(修订)
|
||||
|
||||
| 维度 | 桌面(Wails) | iOS(arch A) |
|
||||
|
||||
Reference in New Issue
Block a user