Skip to main content

glass-effects

Apple glass/translucency effect integration via NSVisualEffectView (macOS) and UIVisualEffectView (iOS)

Quellinformationen

Repository
amberframework/asset_pipeline
Letzte Quellaktivität
18. Februar 2026 um 11:55
Erkannte Sprache von SKILL.md
Englisch
Sterne
5
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
glass-effects
description
Apple glass/translucency effect integration via NSVisualEffectView (macOS) and UIVisualEffectView (iOS)
version
1.0
# Apple Glass Effect Integration This skill covers how to use Apple's vibrancy/translucency glass effects from Crystal via the ObjC runtime bridge. Glass effects allow UI to blur and tint the content behind a window or behind another view, creating the frosted-glass appearance used throughout macOS and iOS. ## macOS: NSVisualEffectView `NSVisualEffectView` is the macOS class for glass/translucency effects. It blurs and tints content behind the view or behind the window. ### Three Key Properties | Property | Selector | Type | Description | |----------|----------|------|-------------| | Material | `setMaterial:` | `NSInteger` | What the effect looks like (color/tint) | | Blending Mode | `setBlendingMode:` | `NSInteger` | What gets blurred (window background or view behind) | | State | `setState:` | `NSInteger` | When the effect is active | ### Material Constants | Value | Name | Appearance | |-------|------|------------| | 0 | `NSVisualEffectMaterialAppearanceBased` | Matches app appearance (deprecated) | | 1 | `NSVisualEffectMaterialLight` | Light translucent (deprecated, use Titlebar) | | 2 | `NSVisualEffectMaterialDark` | Dark translucent (deprecated, use HUDWindow) | | 3 | `NSVisualEffectMaterialTitlebar` | Window title bar appearance | | 4 | `NSVisualEffectMaterialSelection` | Selected content appearance | | 5 | `NSVisualEffectMaterialMenu` | Menu background | | 6 | `NSVisualEffectMaterialPopover` | Popover background | | 7 | `NSVisualEffectMaterialSidebar` | Sidebar background | | 8 | `NSVisualEffectMaterialHeaderView` | Header view background | | 9 | `NSVisualEffectMaterialSheet` | Sheet background | | 10 | `NSVisualEffectMaterialWindowBackground` | Standard window background | | 11 | `NSVisualEffectMaterialHUDWindow` | HUD window (heads-up display) | | 12 | `NSVisualEffectMaterialFullScreenUI` | Full screen UI background | | 13 | `NSVisualEffectMaterialToolTip` | Tooltip background | | 14 | `NSVisualEffectMaterialContentBackground` | Content area background | | 15 | `NSVisualEffectMaterialUnderWindowBackground` | Under-window background | | 16 | `NSVisualEffectMaterialUnderPageBackground` | Under-page background | ### Blending Mode Constants | Value | Name | Description | |-------|------|-------------| | 0 | `BehindWindow` | Blurs content behind the window (desktop, other windows) | | 1 | `WithinWindow` | Blurs content behind this view within the same window | ### State Constants | Value | Name | Description | |-------|------|-------------| | 0 | `FollowsWindowActiveState` | Effect active only when window is key/main | | 1 | `Active` | Effect always active, even when window is not focused | | 2 | `Inactive` | Effect always inactive (appears opaque) | ### Crystal Example: macOS Glass Window ```crystal # Create the visual effect view filling the content area content_view = LibObjC.objc_send(window, sel("contentView")) content_frame = LibObjC.objc_get_frame(content_view) visual_effect = LibObjC.objc_send_rect( alloc("NSVisualEffectView"), sel("initWithFrame:"), content_frame) # Material: HUDWindow (dark translucent panel) LibObjC.objc_send_long(visual_effect, sel("setMaterial:"), 11_i64) # Blending: behind the window (blurs desktop and other windows) LibObjC.objc_send_long(visual_effect, sel("setBlendingMode:"), 0_i64) # State: always active (even when window loses focus) LibObjC.objc_send_long(visual_effect, sel("setState:"), 1_i64) # Fill entire content area on resize # Width-sizable (2) | Height-sizable (16) = 18 LibObjC.objc_set_autoresize(visual_effect, 18_u64) # Add as subview of the content view LibObjC.objc_add_subview(content_view, visual_effect) # Now add UI elements as children of the visual effect view LibObjC.objc_add_subview(visual_effect, title_label) LibObjC.objc_add_subview(visual_effect, button) ``` ### Sidebar Glass Pattern A common pattern for apps with a sidebar: ```crystal # Sidebar with sidebar material sidebar = LibObjC.objc_send_rect( alloc("NSVisualEffectView"), sel("initWithFrame:"), LibObjC::CGRect.new(x: 0.0, y: 0.0, width: 240.0, height: 600.0)) LibObjC.objc_send_long(sidebar, sel("setMaterial:"), 7_i64) # Sidebar LibObjC.objc_send_long(sidebar, sel("setBlendingMode:"), 0_i64) LibObjC.objc_send_long(sidebar, sel("setState:"), 0_i64) # Follow window state ``` --- ## iOS: UIVisualEffectView `UIVisualEffectView` is the iOS equivalent. It takes a `UIVisualEffect` object (either `UIBlurEffect` or `UIVibrancyEffect`) rather than individual material/blending/state properties. ### UIBlurEffect Styles | Value | Name | Appearance | |-------|------|------------| | 0 | `UIBlurEffectStyleExtraLight` | Extra light blur | | 1 | `UIBlurEffectStyleLight` | Light blur | | 2 | `UIBlurEffectStyleDark` | Dark blur | | 3 | `UIBlurEffectStyleRegular` | Adapts to light/dark mode (iOS 10+) | | 4 | `UIBlurEffectStyleProminent` | Adapts, more opaque than Regular (iOS 10+) | | 10 | `UIBlurEffectStyleSystemMaterial` | System material (iOS 13+) | | 11 | `UIBlurEffectStyleSystemMaterialLight` | System material, light | | 12 | `UIBlurEffectStyleSystemMaterialDark` | System material, dark | | 13 | `UIBlurEffectStyleSystemThinMaterial` | Thin system material | | 14 | `UIBlurEffectStyleSystemUltraThinMaterial` | Ultra thin system material | | 15 | `UIBlurEffectStyleSystemThickMaterial` | Thick system material | | 16 | `UIBlurEffectStyleSystemChromeMaterial` | Chrome-style system material | ### Crystal Example: iOS Glass ```crystal # Create a blur effect blur_cls = LibObjC.objc_getClass("UIBlurEffect") blur_effect = LibObjC.objc_send_long( blur_cls.as(LibObjC::Id), sel("effectWithStyle:"), 3_i64) # Regular # Create the visual effect view with the blur visual_effect = LibObjC.objc_send_id( alloc("UIVisualEffectView"), sel("initWithEffect:"), blur_effect) # Set frame (or use Auto Layout) LibObjC.objc_send_rect_void(visual_effect, sel("setFrame:"), LibObjC::CGRect.new(x: 0.0, y: 0.0, width: 375.0, height: 667.0)) # Add UI elements to the contentView of the visual effect view content_view = LibObjC.objc_send(visual_effect, sel("contentView")) LibObjC.objc_add_subview(content_view, label) ``` ### Vibrancy Effect (Text/Icons on Glass) For text and icons that should vibrate (change opacity/blend) with the glass: ```crystal # First create the blur effect blur_effect = LibObjC.objc_send_long( LibObjC.objc_getClass("UIBlurEffect").as(LibObjC::Id), sel("effectWithStyle:"), 3_i64) # Create vibrancy effect referencing the blur vibrancy_cls = LibObjC.objc_getClass("UIVibrancyEffect") vibrancy_effect = LibObjC.objc_send_id( vibrancy_cls.as(LibObjC::Id), sel("effectForBlurEffect:"), blur_effect) # Create a secondary visual effect view for vibrant content vibrant_view = LibObjC.objc_send_id( alloc("UIVisualEffectView"), sel("initWithEffect:"), vibrancy_effect) # Add vibrant content (labels, icons) to this view's contentView vibrant_content = LibObjC.objc_send(vibrant_view, sel("contentView")) LibObjC.objc_add_subview(vibrant_content, label) # Add the vibrant view inside the blur view's contentView blur_content = LibObjC.objc_send(blur_visual_effect_view, sel("contentView")) LibObjC.objc_add_subview(blur_content, vibrant_view) ``` --- ## ARM64 Calling Convention Considerations Glass effects involve selectors with multiple `Float64` (CGFloat) arguments, which makes the ARM64 calling convention critical. ### The Problem On ARM64, `objc_msgSend` is a raw assembly trampoline. It does not know the types of arguments. The **caller** must set up registers correctly: - **Integer/pointer arguments:** `x0` (self), `x1` (_cmd), `x2`-`x7` (method args) - **Float/double arguments:** `d0`-`d7` (independent register bank) - **CGRect:** Passed as HFA (Homogeneous Floating-point Aggregate) in `d0`-`d3` If you cast `objc_msgSend` to a function pointer with fewer double parameters than the actual ObjC method expects, the higher `d` registers contain garbage from prior computations. This produces random colors, invisible views (alpha = 0.0 or NaN), or crashes. ### The Solution Every unique combination of (return type, parameter types) has a dedicated C wrapper function in `objc_bridge.c`. Key wrappers used for glass effects: ```c // Material, blending, state (all take NSInteger = long) void* objc_send_long(void* self, SEL sel, long arg1); // NSVisualEffectView initWithFrame: (CGRect = 4 doubles as HFA) void* objc_send_rect(void* self, SEL sel, CGRect rect); // Autoresize mask (NSUInteger = unsigned long) void objc_set_autoresize(void* view, unsigned long mask); // NSColor creation (4 doubles: r, g, b, a) void* nscolor_rgba(double r, double g, double b, double a); void* nscolor_white_alpha(double white, double alpha); ``` ### Rule Never cast `objc_msgSend` directly in Crystal with a generic function pointer. Always go through the typed wrapper in `objc_bridge.c`. If a new ObjC method needs a combination of argument types not covered by existing wrappers, add a new wrapper function to the bridge. ### Existing Typed Wrappers for Float/Double Arguments | Wrapper | Signature | Use case | |---------|-----------|----------| | `objc_send_1d` | `(id, SEL, d0)` | Single double arg (e.g., `setAlphaValue:`) | | `objc_send_2d_ret_id` | `(id, SEL, d0, d1) -> id` | Two doubles returning object | | `objc_send_4d_ret_id` | `(id, SEL, d0, d1, d2, d3) -> id` | Four doubles returning object (color creation) | | `objc_send_rect` | `(id, SEL, CGRect) -> id` | CGRect HFA argument (frame init) | | `objc_send_rect_void` | `(id, SEL, CGRect)` | CGRect HFA, no return (setFrame:) | | `nscolor_rgba` | `(r, g, b, a) -> id` | NSColor RGBA creation | | `nscolor_srgba` | `(r, g, b, a) -> id` | NSColor sRGB creation | | `nscolor_white_alpha` | `(w, a) -> id` | NSColor grayscale creation | --- ## Integration with the UI Component System When glass effects are integrated into the cross-platform `UI::View` system, they would be handled at the renderer level rather than as a view type. For example, the `AppKit::Renderer` could apply glass effects to any view with a specific `background` configuration: ```crystal # Future API concept stack = UI::VStack.new(spacing: 12.0) stack.background = UI::GlassBackground.new( material: :sidebar, blending: :behind_window ) ``` The web renderer would map this to CSS `backdrop-filter: blur()`, the AppKit renderer to `NSVisualEffectView`, and the UIKit renderer to `UIVisualEffectView`. --- ## Working Demo Reference A complete working macOS app with glass effects is at: ``` /Users/crimsonknight/open_source_coding_projects/crystal/samples/cross_platform/macos_app.cr ``` Build and run: ```bash cd /Users/crimsonknight/open_source_coding_projects/crystal/samples/cross_platform clang -c objc_bridge.c -o objc_bridge.o bin/crystal build macos_app.cr --link-flags="objc_bridge.o" -o macos_app ./macos_app ``` This demo creates a window with `NSVisualEffectMaterialHUDWindow` (material 13), `BehindWindow` blending (0), and `Active` state (1), showing Crystal-generated labels and a button on top of the glass surface.
Auf GitHub ansehen