@mmerterden/multi-agent-pipeline 20.17.0 → 20.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/docs/facts.json +6 -6
- package/docs/features.md +1 -1
- package/install/catalog-history.json +1 -1
- package/manifest.json +85 -47
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/update/SKILL.md +2 -2
- package/pipeline/multi-agent-refs/features/usage-reporting.md +106 -67
- package/pipeline/schemas/prefs.schema.json +23 -2
- package/pipeline/scripts/usage-identity.mjs +312 -53
- package/pipeline/scripts/usage-register.mjs +204 -85
- package/pipeline/scripts/usage-report.mjs +7 -5
- package/pipeline/skills/.skill-manifest.json +35 -15
- package/pipeline/skills/.skills-index.json +66 -11
- package/pipeline/skills/shared/README.md +18 -13
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +2 -2
- package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +3 -1
- package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +2 -1
- package/pipeline/skills/shared/external/hig-app-icons/SKILL.md +159 -0
- package/pipeline/skills/shared/external/hig-app-icons/evals/evals.json +48 -0
- package/pipeline/skills/shared/external/hig-app-icons/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +115 -0
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +32 -4
- package/pipeline/skills/shared/external/hig-components-layout/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/hig-components-layout/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +231 -0
- package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +5 -30
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +23 -4
- package/pipeline/skills/shared/external/hig-foundations/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/hig-foundations/evals/trigger-eval.json +21 -0
- package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +34 -26
- package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +219 -0
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +5 -4
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +19 -3
- package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +188 -0
- package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +5 -11
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +17 -5
- package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +218 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +4 -1
- package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +135 -0
- package/pipeline/skills/shared/external/ios-design-review/SKILL.md +146 -0
- package/pipeline/skills/shared/external/ios-design-review/evals/evals.json +48 -0
- package/pipeline/skills/shared/external/ios-design-review/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +135 -0
- package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +193 -0
- package/pipeline/skills/shared/external/ios-haptics/SKILL.md +191 -0
- package/pipeline/skills/shared/external/ios-haptics/evals/evals.json +49 -0
- package/pipeline/skills/shared/external/ios-haptics/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +215 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/NOTICE.md +53 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/SKILL.md +170 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/evals/evals.json +50 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +212 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +145 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +122 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/SKILL.md +177 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/evals/evals.json +49 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +135 -0
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +38 -50
- package/pipeline/skills/shared/external/swiftui-animation/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/swiftui-animation/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +7 -73
- package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +185 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +6 -6
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +3 -253
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +4 -47
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +3 -111
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/liquid-glass.md +11 -381
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +184 -224
- package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +246 -33
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +227 -0
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +15 -18
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/components-index.md +1 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/haptics.md +6 -68
- package/pipeline/skills/skills-index.md +16 -11
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +0 -786
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
# Liquid Glass API Reference
|
|
2
2
|
|
|
3
3
|
Signatures and behaviour behind the `swiftui-liquid-glass` skill. Read its
|
|
4
|
-
SKILL.md for
|
|
4
|
+
SKILL.md first for where glass belongs and the workflow; this file is the
|
|
5
|
+
SwiftUI detail. UIKit is in [uikit-glass.md](uikit-glass.md).
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- [Overview](#overview)
|
|
10
|
+
- [glassEffect(_:in:)](#glasseffect_in)
|
|
11
|
+
- [Glass](#glass)
|
|
12
|
+
- [Clear glass and the dimming layer](#clear-glass-and-the-dimming-layer)
|
|
13
|
+
- [GlassEffectContainer](#glasseffectcontainer)
|
|
14
|
+
- [glassEffectID(_:in:)](#glasseffectid_in)
|
|
15
|
+
- [glassEffectUnion(id:namespace:)](#glasseffectunionidnamespace)
|
|
16
|
+
- [glassEffectTransition(_:) and GlassEffectTransition](#glasseffecttransition_-and-glasseffecttransition)
|
|
17
|
+
- [Button styles](#button-styles)
|
|
18
|
+
- [DefaultGlassEffectShape](#defaultglasseffectshape)
|
|
19
|
+
- [Concentric shapes](#concentric-shapes)
|
|
20
|
+
- [Custom bars: safeAreaBar](#custom-bars-safeareabar)
|
|
21
|
+
- [Scroll edge effect](#scroll-edge-effect)
|
|
22
|
+
- [Background extension (split views)](#background-extension-split-views)
|
|
23
|
+
- [Availability gating](#availability-gating)
|
|
24
|
+
- [Performance guidelines](#performance-guidelines)
|
|
25
|
+
- [Examples](#examples)
|
|
26
|
+
- [Apple documentation links](#apple-documentation-links)
|
|
5
27
|
|
|
6
28
|
## Overview
|
|
7
29
|
|
|
@@ -53,6 +75,33 @@ struct Glass: Equatable, Sendable {
|
|
|
53
75
|
`interactive(_:)` hands back one that responds to fingers and pointer hover.
|
|
54
76
|
Being `Sendable`, a `Glass` value can be computed anywhere and handed to a view.
|
|
55
77
|
|
|
78
|
+
## Clear glass and the dimming layer
|
|
79
|
+
|
|
80
|
+
`.clear` is highly translucent and meant for components that float over
|
|
81
|
+
photos and video. When the media behind it is bright, put a dark dimming layer
|
|
82
|
+
underneath; Apple's HIG suggests 35% black. Over dark media, or with AVKit's
|
|
83
|
+
standard playback controls, skip it.
|
|
84
|
+
|
|
85
|
+
```swift
|
|
86
|
+
@available(iOS 26, *)
|
|
87
|
+
struct PhotoOverlayControls: View {
|
|
88
|
+
var body: some View {
|
|
89
|
+
HStack(spacing: 16) {
|
|
90
|
+
Button("Share", systemImage: "square.and.arrow.up") { }
|
|
91
|
+
Button("Favorite", systemImage: "heart") { }
|
|
92
|
+
}
|
|
93
|
+
.labelStyle(.iconOnly)
|
|
94
|
+
.font(.title3)
|
|
95
|
+
.padding()
|
|
96
|
+
.glassEffect(.clear.interactive())
|
|
97
|
+
.background(.black.opacity(0.35), in: .capsule)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Choose `.regular` instead whenever the component carries much text or sits
|
|
103
|
+
over content whose brightness you do not control.
|
|
104
|
+
|
|
56
105
|
## GlassEffectContainer
|
|
57
106
|
|
|
58
107
|
A generic `View` over its `Content`, isolated to the main actor (declared
|
|
@@ -134,12 +183,78 @@ a `Button` yourself.
|
|
|
134
183
|
|
|
135
184
|
The default `in:` argument of `glassEffect`. It resolves to a `Capsule`.
|
|
136
185
|
|
|
186
|
+
## Concentric shapes
|
|
187
|
+
|
|
188
|
+
`ConcentricRectangle` (iOS 26) is a shape whose corners can be concentric with
|
|
189
|
+
a container's corners, fixed, or square. A corner is concentric when its radius
|
|
190
|
+
shares a center with the container's rounded corner, which is how sheets
|
|
191
|
+
follow the curve of the screen.
|
|
192
|
+
|
|
193
|
+
| Initializer or style | Result |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `ConcentricRectangle()` | Every corner concentric with the container |
|
|
196
|
+
| `ConcentricRectangle(corners: .concentric(minimum: 12), isUniform: true)` | Concentric, never below 12 points, all corners equal |
|
|
197
|
+
| `ConcentricRectangle(uniformTopCorners: .fixed(12), uniformBottomCorners: .concentric)` | Fixed top, concentric bottom, like a format sheet |
|
|
198
|
+
|
|
199
|
+
The container is the device edge, a system container such as a sheet, or any
|
|
200
|
+
view that sets `containerShape(_:)` with a `RoundedRectangularShape`
|
|
201
|
+
(`RoundedRectangle`, `Capsule`, `Circle`, `Rectangle`). When a corner sits far
|
|
202
|
+
from the container's corner the computed radius can be zero, so use
|
|
203
|
+
`.concentric(minimum:)` where the corner must stay rounded.
|
|
204
|
+
|
|
205
|
+
```swift
|
|
206
|
+
@available(iOS 26, *)
|
|
207
|
+
struct NestedPanel: View {
|
|
208
|
+
var body: some View {
|
|
209
|
+
VStack(spacing: 12) {
|
|
210
|
+
Text("Boarding").font(.headline)
|
|
211
|
+
Button("Show pass") { }
|
|
212
|
+
.buttonStyle(.glassProminent)
|
|
213
|
+
}
|
|
214
|
+
.padding(16)
|
|
215
|
+
.background(.thinMaterial, in: ConcentricRectangle(corners: .concentric(minimum: 12), isUniform: true))
|
|
216
|
+
.containerShape(RoundedRectangle(cornerRadius: 28))
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
HIG asks custom components in toolbars to keep their corner radius
|
|
222
|
+
concentric with the bar, as standard components do.
|
|
223
|
+
|
|
224
|
+
## Custom bars: safeAreaBar
|
|
225
|
+
|
|
226
|
+
`safeAreaBar(edge:alignment:spacing:content:)` (iOS 26) works like
|
|
227
|
+
`safeAreaInset`, and also extends the scroll edge effect of scroll views
|
|
228
|
+
under the inset, so a custom bar gets the same legibility treatment as a
|
|
229
|
+
system toolbar.
|
|
230
|
+
|
|
231
|
+
```swift
|
|
232
|
+
@available(iOS 26, *)
|
|
233
|
+
struct ComposerScreen: View {
|
|
234
|
+
@State private var draft = ""
|
|
235
|
+
|
|
236
|
+
var body: some View {
|
|
237
|
+
ScrollView { MessageList() }
|
|
238
|
+
.safeAreaBar(edge: .bottom) {
|
|
239
|
+
HStack {
|
|
240
|
+
TextField("Message", text: $draft)
|
|
241
|
+
Button("Send", systemImage: "paperplane.fill") { }
|
|
242
|
+
.buttonStyle(.glassProminent)
|
|
243
|
+
}
|
|
244
|
+
.padding()
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
137
250
|
## Scroll edge effect
|
|
138
251
|
|
|
139
|
-
|
|
140
|
-
`for:`, an `Edge.Set`. Styles are `.automatic`, `.soft` (a gradual fade)
|
|
141
|
-
|
|
142
|
-
|
|
252
|
+
`scrollEdgeEffectStyle(_:for:)` takes an optional `ScrollEdgeEffectStyle` and,
|
|
253
|
+
after `for:`, an `Edge.Set`. Styles are `.automatic`, `.soft` (a gradual fade)
|
|
254
|
+
and `.hard` (a crisper cut off). `scrollEdgeEffectHidden(_:for:)` hides the
|
|
255
|
+
effect. System bars already apply an edge effect; adjust it for custom bars
|
|
256
|
+
that float over scrolling content. Remove custom darkening gradients behind
|
|
257
|
+
bar items, which fight the effect.
|
|
143
258
|
|
|
144
259
|
## Background extension (split views)
|
|
145
260
|
|
|
@@ -162,17 +277,6 @@ NavigationSplitView {
|
|
|
162
277
|
}
|
|
163
278
|
```
|
|
164
279
|
|
|
165
|
-
## ToolbarSpacer
|
|
166
|
-
|
|
167
|
-
```swift
|
|
168
|
-
struct ToolbarSpacer: ToolbarContent {
|
|
169
|
-
init(_ sizing: SpacerSizing = .flexible, placement: ToolbarItemPlacement = .automatic)
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
`.fixed` inserts a fixed gap that separates glass groups; `.flexible` pushes
|
|
174
|
-
groups apart. Available on iOS and macOS 26.
|
|
175
|
-
|
|
176
280
|
## Availability gating
|
|
177
281
|
|
|
178
282
|
Every API above needs iOS 26. Give older systems a material background of the
|
|
@@ -198,27 +302,131 @@ extension View {
|
|
|
198
302
|
- Prefer one container per group over many small containers.
|
|
199
303
|
- Measure with Instruments (SwiftUI and Core Animation templates) before and
|
|
200
304
|
after adding glass to scrolling content.
|
|
305
|
+
- Apple notes that combining custom glass in a `GlassEffectContainer` helps
|
|
306
|
+
rendering performance as well as morphing.
|
|
307
|
+
|
|
308
|
+
## Examples
|
|
309
|
+
|
|
310
|
+
### A gated glass card for a floating summary
|
|
201
311
|
|
|
202
|
-
|
|
312
|
+
```swift
|
|
313
|
+
struct StatusPill: View {
|
|
314
|
+
let title: String
|
|
315
|
+
|
|
316
|
+
var body: some View {
|
|
317
|
+
let label = Text(title).font(.headline).padding(20)
|
|
203
318
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
319
|
+
if #available(iOS 26, *) {
|
|
320
|
+
label.glassEffect(.regular.interactive(), in: .rect(cornerRadius: 18))
|
|
321
|
+
} else {
|
|
322
|
+
label.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 18))
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
210
327
|
|
|
211
|
-
|
|
328
|
+
### A row of glass controls
|
|
212
329
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
330
|
+
Container spacing matches the stack spacing so the circles blend only when
|
|
331
|
+
they move toward each other.
|
|
332
|
+
|
|
333
|
+
```swift
|
|
334
|
+
@available(iOS 26, *)
|
|
335
|
+
struct PlaybackControls: View {
|
|
336
|
+
private let symbols = ["backward.fill", "play.fill", "forward.fill"]
|
|
337
|
+
private let gap: CGFloat = 20
|
|
338
|
+
|
|
339
|
+
var body: some View {
|
|
340
|
+
GlassEffectContainer(spacing: gap) {
|
|
341
|
+
HStack(spacing: gap) {
|
|
342
|
+
ForEach(symbols, id: \.self) { symbol in
|
|
343
|
+
Image(systemName: symbol)
|
|
344
|
+
.font(.title2)
|
|
345
|
+
.frame(width: 60, height: 60)
|
|
346
|
+
.glassEffect(.regular.interactive())
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Morphing between states
|
|
355
|
+
|
|
356
|
+
```swift
|
|
357
|
+
@available(iOS 26, *)
|
|
358
|
+
struct ExpandingActions: View {
|
|
359
|
+
@Namespace private var glassSpace
|
|
360
|
+
@State private var expanded = false
|
|
361
|
+
|
|
362
|
+
var body: some View {
|
|
363
|
+
let gap: CGFloat = 36
|
|
364
|
+
return GlassEffectContainer(spacing: gap) {
|
|
365
|
+
HStack(spacing: gap) {
|
|
366
|
+
Button("Compose", systemImage: "square.and.pencil") {
|
|
367
|
+
withAnimation(.bouncy) { expanded.toggle() }
|
|
368
|
+
}
|
|
369
|
+
.buttonStyle(.glass)
|
|
370
|
+
.glassEffectID("compose", in: glassSpace)
|
|
371
|
+
|
|
372
|
+
if expanded {
|
|
373
|
+
Button("Attach", systemImage: "paperclip") { }
|
|
374
|
+
.buttonStyle(.glass)
|
|
375
|
+
.glassEffectID("attach", in: glassSpace)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Merging shapes with a union
|
|
384
|
+
|
|
385
|
+
```swift
|
|
386
|
+
@available(iOS 26, *)
|
|
387
|
+
struct FilterChips: View {
|
|
388
|
+
@Namespace private var chipSpace
|
|
389
|
+
let filters = ["Today", "Week", "Month", "Year"]
|
|
390
|
+
|
|
391
|
+
var body: some View {
|
|
392
|
+
let gap: CGFloat = 12
|
|
393
|
+
return GlassEffectContainer(spacing: gap) {
|
|
394
|
+
HStack(spacing: gap) {
|
|
395
|
+
ForEach(filters.indices, id: \.self) { index in
|
|
396
|
+
Text(filters[index])
|
|
397
|
+
.padding(.horizontal, 14)
|
|
398
|
+
.padding(.vertical, 8)
|
|
399
|
+
.glassEffect()
|
|
400
|
+
.glassEffectUnion(id: index < 2 ? "short" : "long", namespace: chipSpace)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Chips 0 and 1 render as one glass shape and chips 2 and 3 as another.
|
|
409
|
+
|
|
410
|
+
### A solid fallback under Reduce Transparency
|
|
411
|
+
|
|
412
|
+
System glass adapts on its own. A custom surface that must stay legible over
|
|
413
|
+
busy content can switch to a solid fill when the setting is on:
|
|
414
|
+
|
|
415
|
+
```swift
|
|
416
|
+
@available(iOS 26, *)
|
|
417
|
+
struct MapControlStrip: View {
|
|
418
|
+
@Environment(\.accessibilityReduceTransparency) private var reduceTransparency
|
|
419
|
+
|
|
420
|
+
var body: some View {
|
|
421
|
+
let controls = HStack { LocateButton(); LayersButton() }.padding(12)
|
|
422
|
+
if reduceTransparency {
|
|
423
|
+
controls.background(Color(uiColor: .secondarySystemBackground), in: .capsule)
|
|
424
|
+
} else {
|
|
425
|
+
controls.glassEffect(.regular.interactive())
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
222
430
|
|
|
223
431
|
## Apple documentation links
|
|
224
432
|
|
|
@@ -231,3 +439,8 @@ extension View {
|
|
|
231
439
|
- [GlassEffectTransition](https://developer.apple.com/documentation/swiftui/glasseffecttransition)
|
|
232
440
|
- [GlassButtonStyle](https://developer.apple.com/documentation/swiftui/glassbuttonstyle)
|
|
233
441
|
- [GlassProminentButtonStyle](https://developer.apple.com/documentation/swiftui/glassprominentbuttonstyle)
|
|
442
|
+
- [ConcentricRectangle](https://developer.apple.com/documentation/swiftui/concentricrectangle)
|
|
443
|
+
- [safeAreaBar(edge:alignment:spacing:content:)](https://developer.apple.com/documentation/swiftui/view/safeareabar(edge:alignment:spacing:content:))
|
|
444
|
+
- [scrollEdgeEffectStyle(_:for:)](https://developer.apple.com/documentation/swiftui/view/scrolledgeeffectstyle(_:for:))
|
|
445
|
+
- [backgroundExtensionEffect()](https://developer.apple.com/documentation/swiftui/view/backgroundextensioneffect())
|
|
446
|
+
- [HIG: Materials](https://developer.apple.com/design/human-interface-guidelines/materials)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Liquid Glass in UIKit
|
|
2
|
+
|
|
3
|
+
UIKit side of the `swiftui-liquid-glass` skill. Where glass belongs, regular
|
|
4
|
+
versus clear, and the accessibility rules are in `../SKILL.md` and apply
|
|
5
|
+
unchanged here. Every API in this file is iOS, iPadOS, Mac Catalyst and tvOS
|
|
6
|
+
26.0 unless noted.
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Standard components first](#standard-components-first)
|
|
11
|
+
- [UIGlassEffect](#uiglasseffect)
|
|
12
|
+
- [UIGlassContainerEffect](#uiglasscontainereffect)
|
|
13
|
+
- [Glass buttons](#glass-buttons)
|
|
14
|
+
- [Corners: UICornerConfiguration](#corners-uicornerconfiguration)
|
|
15
|
+
- [Scroll edge effects](#scroll-edge-effects)
|
|
16
|
+
- [UIBackgroundExtensionView](#uibackgroundextensionview)
|
|
17
|
+
- [Fallback and accessibility](#fallback-and-accessibility)
|
|
18
|
+
- [Opting out for one release](#opting-out-for-one-release)
|
|
19
|
+
- [Links](#links)
|
|
20
|
+
|
|
21
|
+
## Standard components first
|
|
22
|
+
|
|
23
|
+
`UINavigationBar`, `UITabBar`, `UIToolbar`, sheets, popovers and standard
|
|
24
|
+
controls adopt the material when built with the 26 SDK. Remove custom bar
|
|
25
|
+
background images and appearance colors that sat behind items: they overlay
|
|
26
|
+
the glass and the scroll edge effect. Popovers that add their own
|
|
27
|
+
`UIVisualEffectView` background should drop it.
|
|
28
|
+
|
|
29
|
+
## UIGlassEffect
|
|
30
|
+
|
|
31
|
+
A `UIVisualEffect` subclass rendering the glass material, hosted in a
|
|
32
|
+
`UIVisualEffectView`. Content goes in the effect view's `contentView`.
|
|
33
|
+
|
|
34
|
+
| Member | Meaning |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `init(style:)` | `UIGlassEffect.Style.regular` or `.clear` |
|
|
37
|
+
| `isInteractive` | Reacts to touch like system glass controls |
|
|
38
|
+
| `tintColor` | Colors the glass for prominence; keep for one primary action or a status |
|
|
39
|
+
|
|
40
|
+
```swift
|
|
41
|
+
@available(iOS 26, *)
|
|
42
|
+
@MainActor
|
|
43
|
+
func makeGlassBadge(text: String) -> UIVisualEffectView {
|
|
44
|
+
let glass = UIGlassEffect(style: .regular)
|
|
45
|
+
glass.isInteractive = true
|
|
46
|
+
|
|
47
|
+
let glassView = UIVisualEffectView(effect: glass)
|
|
48
|
+
glassView.cornerConfiguration = .capsule()
|
|
49
|
+
|
|
50
|
+
let label = UILabel()
|
|
51
|
+
label.text = text
|
|
52
|
+
label.font = .preferredFont(forTextStyle: .headline)
|
|
53
|
+
label.adjustsFontForContentSizeCategory = true
|
|
54
|
+
label.translatesAutoresizingMaskIntoConstraints = false
|
|
55
|
+
glassView.contentView.addSubview(label)
|
|
56
|
+
|
|
57
|
+
NSLayoutConstraint.activate([
|
|
58
|
+
label.leadingAnchor.constraint(equalTo: glassView.contentView.leadingAnchor, constant: 16),
|
|
59
|
+
label.trailingAnchor.constraint(equalTo: glassView.contentView.trailingAnchor, constant: -16),
|
|
60
|
+
label.topAnchor.constraint(equalTo: glassView.contentView.topAnchor, constant: 8),
|
|
61
|
+
label.bottomAnchor.constraint(equalTo: glassView.contentView.bottomAnchor, constant: -8),
|
|
62
|
+
])
|
|
63
|
+
return glassView
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The same placement rule applies as in SwiftUI: navigation and controls only.
|
|
68
|
+
|
|
69
|
+
## UIGlassContainerEffect
|
|
70
|
+
|
|
71
|
+
Renders several glass elements as one combined effect, so they blend and merge
|
|
72
|
+
when close, the UIKit counterpart of `GlassEffectContainer`. Put a
|
|
73
|
+
`UIVisualEffectView` with a `UIGlassContainerEffect` around nested
|
|
74
|
+
`UIVisualEffectView`s that each use a `UIGlassEffect`, added to the
|
|
75
|
+
container's `contentView`. `spacing` sets the distance at which neighbours
|
|
76
|
+
start to merge.
|
|
77
|
+
|
|
78
|
+
```swift
|
|
79
|
+
@available(iOS 26, *)
|
|
80
|
+
@MainActor
|
|
81
|
+
func makeControlCluster(_ buttons: [UIView]) -> UIVisualEffectView {
|
|
82
|
+
let containerEffect = UIGlassContainerEffect()
|
|
83
|
+
containerEffect.spacing = 12
|
|
84
|
+
let container = UIVisualEffectView(effect: containerEffect)
|
|
85
|
+
|
|
86
|
+
let stack = UIStackView()
|
|
87
|
+
stack.axis = .horizontal
|
|
88
|
+
stack.spacing = 12
|
|
89
|
+
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
90
|
+
|
|
91
|
+
for button in buttons {
|
|
92
|
+
let glassView = UIVisualEffectView(effect: UIGlassEffect(style: .regular))
|
|
93
|
+
glassView.cornerConfiguration = .capsule()
|
|
94
|
+
button.translatesAutoresizingMaskIntoConstraints = false
|
|
95
|
+
glassView.contentView.addSubview(button)
|
|
96
|
+
NSLayoutConstraint.activate([
|
|
97
|
+
button.centerXAnchor.constraint(equalTo: glassView.contentView.centerXAnchor),
|
|
98
|
+
button.centerYAnchor.constraint(equalTo: glassView.contentView.centerYAnchor),
|
|
99
|
+
glassView.widthAnchor.constraint(equalToConstant: 48),
|
|
100
|
+
glassView.heightAnchor.constraint(equalToConstant: 48),
|
|
101
|
+
])
|
|
102
|
+
stack.addArrangedSubview(glassView)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
container.contentView.addSubview(stack)
|
|
106
|
+
NSLayoutConstraint.activate([
|
|
107
|
+
stack.leadingAnchor.constraint(equalTo: container.contentView.leadingAnchor),
|
|
108
|
+
stack.trailingAnchor.constraint(equalTo: container.contentView.trailingAnchor),
|
|
109
|
+
stack.topAnchor.constraint(equalTo: container.contentView.topAnchor),
|
|
110
|
+
stack.bottomAnchor.constraint(equalTo: container.contentView.bottomAnchor),
|
|
111
|
+
])
|
|
112
|
+
return container
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Glass buttons
|
|
117
|
+
|
|
118
|
+
Prefer a configuration to building glass around a `UIButton` by hand.
|
|
119
|
+
|
|
120
|
+
| Configuration | Use |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `UIButton.Configuration.glass()` | Regular glass button |
|
|
123
|
+
| `.prominentGlass()` | The primary action |
|
|
124
|
+
| `.clearGlass()` | Over rich media |
|
|
125
|
+
| `.prominentClearGlass()` | Primary action over rich media |
|
|
126
|
+
|
|
127
|
+
```swift
|
|
128
|
+
@available(iOS 26, *)
|
|
129
|
+
@MainActor
|
|
130
|
+
func makeDoneButton(action: UIAction) -> UIButton {
|
|
131
|
+
var configuration = UIButton.Configuration.prominentGlass()
|
|
132
|
+
configuration.title = String(localized: "Done")
|
|
133
|
+
return UIButton(configuration: configuration, primaryAction: action)
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Corners: UICornerConfiguration
|
|
138
|
+
|
|
139
|
+
`UIView.cornerConfiguration` (iOS 26, also visionOS 26) takes a
|
|
140
|
+
`UICornerConfiguration` built from `UICornerRadius` values.
|
|
141
|
+
|
|
142
|
+
| Expression | Result |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `.corners(radius: 12)` | Fixed radius on every corner |
|
|
145
|
+
| `.corners(radius: .containerConcentric())` | Concentric with the containing view |
|
|
146
|
+
| `.corners(radius: .containerConcentric(minimum: 10))` | Concentric, never below 10 points |
|
|
147
|
+
| `.capsule()` | Capsule; `maximumRadius:` lets tall views break the capsule |
|
|
148
|
+
| `.uniformTopRadius(_:bottomLeftRadius:bottomRightRadius:)` and siblings | Mixed uniform and independent corners |
|
|
149
|
+
|
|
150
|
+
Squared corners are the default.
|
|
151
|
+
|
|
152
|
+
## Scroll edge effects
|
|
153
|
+
|
|
154
|
+
Each `UIScrollView` has `topEdgeEffect`, `bottomEdgeEffect`, `leftEdgeEffect`
|
|
155
|
+
and `rightEdgeEffect`, each a `UIScrollEdgeEffect` (also visionOS 26) with:
|
|
156
|
+
|
|
157
|
+
- `style`: `UIScrollEdgeEffect.Style.automatic`, `.soft` or `.hard`
|
|
158
|
+
- `isHidden`: turns the effect off for that edge
|
|
159
|
+
|
|
160
|
+
```swift
|
|
161
|
+
scrollView.topEdgeEffect.style = .hard
|
|
162
|
+
scrollView.bottomEdgeEffect.isHidden = false
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A custom bar that floats over the edge of a scroll view registers with
|
|
166
|
+
`UIScrollEdgeElementContainerInteraction`, so its labels, images, glass views
|
|
167
|
+
and controls shape the edge effect:
|
|
168
|
+
|
|
169
|
+
```swift
|
|
170
|
+
let interaction = UIScrollEdgeElementContainerInteraction()
|
|
171
|
+
interaction.scrollView = scrollView
|
|
172
|
+
interaction.edge = .bottom
|
|
173
|
+
buttonContainer.addInteraction(interaction)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## UIBackgroundExtensionView
|
|
177
|
+
|
|
178
|
+
A view that can be laid out past the safe area, for example under a sidebar or
|
|
179
|
+
inspector, while keeping its `contentView` inside the safe area and filling the
|
|
180
|
+
rest with modified copies of the content's edges. It is the UIKit counterpart
|
|
181
|
+
of `backgroundExtensionEffect()`. `automaticallyPlacesContentView` defaults to
|
|
182
|
+
`true`; set it to `false` to position the content view yourself.
|
|
183
|
+
|
|
184
|
+
```swift
|
|
185
|
+
@available(iOS 26, *)
|
|
186
|
+
@MainActor
|
|
187
|
+
func makeHeroBackground(image: UIImage) -> UIBackgroundExtensionView {
|
|
188
|
+
let extensionView = UIBackgroundExtensionView()
|
|
189
|
+
let imageView = UIImageView(image: image)
|
|
190
|
+
imageView.contentMode = .scaleAspectFill
|
|
191
|
+
extensionView.contentView = imageView
|
|
192
|
+
return extensionView
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Use it for the backdrop of a split view's secondary column, not for every
|
|
197
|
+
view.
|
|
198
|
+
|
|
199
|
+
## Fallback and accessibility
|
|
200
|
+
|
|
201
|
+
- Before iOS 26, use `UIVisualEffectView(effect: UIBlurEffect(style: .systemThinMaterial))`
|
|
202
|
+
(or another system material) with the same shape. Gate with
|
|
203
|
+
`if #available(iOS 26, *)`.
|
|
204
|
+
- `UIAccessibility.isReduceTransparencyEnabled` and
|
|
205
|
+
`reduceTransparencyStatusDidChangeNotification` tell a custom surface when to
|
|
206
|
+
swap to an opaque background. `UIAccessibility.isDarkerSystemColorsEnabled`
|
|
207
|
+
reports Increase Contrast.
|
|
208
|
+
- Text inside glass uses Dynamic Type (`preferredFont(forTextStyle:)` with
|
|
209
|
+
`adjustsFontForContentSizeCategory`) and semantic label colors.
|
|
210
|
+
|
|
211
|
+
## Opting out for one release
|
|
212
|
+
|
|
213
|
+
`UIDesignRequiresCompatibility` set to `YES` in Info.plist keeps the
|
|
214
|
+
previous-SDK look while building with the 26 SDK. Apple describes it as
|
|
215
|
+
temporary, and the system ignores it for apps built for the 27 SDKs. See
|
|
216
|
+
`../SKILL.md`.
|
|
217
|
+
|
|
218
|
+
## Links
|
|
219
|
+
|
|
220
|
+
- [UIGlassEffect](https://developer.apple.com/documentation/uikit/uiglasseffect)
|
|
221
|
+
- [UIGlassContainerEffect](https://developer.apple.com/documentation/uikit/uiglasscontainereffect)
|
|
222
|
+
- [UIScrollEdgeEffect](https://developer.apple.com/documentation/uikit/uiscrolledgeeffect)
|
|
223
|
+
- [UIScrollEdgeElementContainerInteraction](https://developer.apple.com/documentation/uikit/uiscrolledgeelementcontainerinteraction)
|
|
224
|
+
- [UIBackgroundExtensionView](https://developer.apple.com/documentation/uikit/uibackgroundextensionview)
|
|
225
|
+
- [UICornerConfiguration](https://developer.apple.com/documentation/uikit/uicornerconfiguration-swift.struct)
|
|
226
|
+
- [UIButton.Configuration.glass()](https://developer.apple.com/documentation/uikit/uibutton/configuration-swift.struct/glass())
|
|
227
|
+
- [UIDesignRequiresCompatibility](https://developer.apple.com/documentation/bundleresources/information-property-list/uidesignrequirescompatibility)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: swiftui-patterns
|
|
3
|
-
description: "SwiftUI
|
|
3
|
+
description: "SwiftUI architecture, state: @Observable, @State, @Bindable, @Environment, view splitting, ViewModifiers, .task, API migration, Writing Tools, Transferable, sharing. Use when structuring an app or owning state. Not for review (swiftui-pro) or visual design (hig-foundations)."
|
|
4
4
|
metadata:
|
|
5
5
|
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
@@ -332,8 +332,7 @@ struct ForecastView: View {
|
|
|
332
332
|
|
|
333
333
|
| API | What it does |
|
|
334
334
|
|---|---|
|
|
335
|
-
| `.scrollEdgeEffectStyle(.soft, for: .top)` |
|
|
336
|
-
| `.backgroundExtensionEffect()` | Extends a mirrored, blurred copy of the view under the safe areas |
|
|
335
|
+
| `.scrollEdgeEffectStyle(.soft, for: .top)`, `.backgroundExtensionEffect()` | Liquid Glass companions; see `swiftui-liquid-glass` |
|
|
337
336
|
| `@Animatable` macro | Synthesises `animatableData` for a type; see `swiftui-animation` |
|
|
338
337
|
| `TextEditor(text:)` with an `AttributedString` binding | Rich text editing |
|
|
339
338
|
|
|
@@ -350,19 +349,20 @@ struct ForecastView: View {
|
|
|
350
349
|
|
|
351
350
|
## 9. Following the HIG
|
|
352
351
|
|
|
353
|
-
|
|
354
|
-
`Color(uiColor: .systemBackground)`) adapt to light and dark mode.
|
|
355
|
-
- Text styles (`.title`, `.headline`, `.body`, `.caption`) scale with Dynamic
|
|
356
|
-
Type.
|
|
357
|
-
- Empty and error states use `ContentUnavailableView`.
|
|
358
|
-
- Leave `spacing:` off stacks. The default `nil` picks platform spacing; pass a
|
|
359
|
-
number only for a deliberate choice.
|
|
360
|
-
- Adapt layout to `horizontalSizeClass`.
|
|
361
|
-
- Give controls an `.accessibilityLabel`, and switch stack axis when
|
|
362
|
-
`dynamicTypeSize.isAccessibilitySize` is true.
|
|
352
|
+
This skill stops at structure and state. Visual rules live elsewhere:
|
|
363
353
|
|
|
364
|
-
|
|
365
|
-
|
|
354
|
+
- Type, color, contrast, spacing and safe areas: `hig-foundations`.
|
|
355
|
+
- Bars, tabs and toolbars: `hig-components-layout`.
|
|
356
|
+
- Empty, error and loading states, onboarding, permission screens:
|
|
357
|
+
`hig-patterns`.
|
|
358
|
+
- Haptics and Core Haptics: `ios-haptics`.
|
|
359
|
+
- Liquid Glass: `swiftui-liquid-glass`.
|
|
360
|
+
- Theming, matched transitions, placeholders and form focus as screen
|
|
361
|
+
recipes: `swiftui-ui-patterns`.
|
|
362
|
+
|
|
363
|
+
Two structural habits still belong here: leave `spacing:` off stacks unless a
|
|
364
|
+
value is deliberate, and keep text sizes and colors semantic so the view code
|
|
365
|
+
does not fight Dynamic Type or dark mode.
|
|
366
366
|
|
|
367
367
|
## 10. Writing Tools (iOS 18+)
|
|
368
368
|
|
|
@@ -453,9 +453,6 @@ final class ReaderPreferences {
|
|
|
453
453
|
the case for MV, when a view model is justified, environment versus init
|
|
454
454
|
injection, testing, app shell wiring, dependency modifier, `ModelContainer`,
|
|
455
455
|
enum sheets, deep links, closure-based clients.
|
|
456
|
-
- [references/design-polish.md](references/design-polish.md): HIG layout,
|
|
457
|
-
typography and color, theming, haptics and Core Haptics, matched transitions,
|
|
458
|
-
loading placeholders, form focus.
|
|
459
456
|
- [references/deprecated-migration.md](references/deprecated-migration.md):
|
|
460
457
|
old API to current API, with the release each replacement needs.
|
|
461
458
|
- [references/platform-and-sharing.md](references/platform-and-sharing.md):
|
|
@@ -22,7 +22,7 @@ Use this file to find component and cross-cutting guidance. Each entry lists whe
|
|
|
22
22
|
- Focus handling: `references/focus.md` - Use for chaining fields and keyboard focus management.
|
|
23
23
|
- Searchable: `references/searchable.md` - Use for native search UI with scopes and async results.
|
|
24
24
|
- Async images and media: `references/media.md` - Use for remote media, previews, and media viewers.
|
|
25
|
-
- Haptics: `
|
|
25
|
+
- Haptics: the `ios-haptics` skill - Use for tactile feedback tied to key actions.
|
|
26
26
|
- Matched transitions: `references/matched-transitions.md` - Use for smooth source-to-destination animations.
|
|
27
27
|
- Deep links and URL routing: `references/deeplinks.md` - Use for in-app navigation from URLs.
|
|
28
28
|
- Title menus: `references/title-menus.md` - Use for filter or context menus in the navigation title.
|