diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..3f57ef1
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,39 @@
+name: Build
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+permissions:
+ contents: read
+
+concurrency:
+ group: build-${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build (${{ matrix.platform }})
+ runs-on: macos-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - platform: iOS
+ destination: generic/platform=iOS Simulator
+ - platform: Mac Catalyst
+ destination: generic/platform=macOS,variant=Mac Catalyst
+ - platform: macOS
+ destination: platform=macOS
+
+ steps:
+ - uses: actions/checkout@v6
+ - name: Build
+ run: >-
+ xcodebuild
+ -scheme CustomNavigationTitle
+ -destination '${{ matrix.destination }}'
+ CODE_SIGNING_ALLOWED=NO
+ build
diff --git a/Assets/demo02.gif b/Assets/demo02.gif
new file mode 100644
index 0000000..0f72a0b
Binary files /dev/null and b/Assets/demo02.gif differ
diff --git a/Examples/CustomNavigationTitleExample/CustomNavigationTitleExample/ContentView.swift b/Examples/CustomNavigationTitleExample/CustomNavigationTitleExample/ContentView.swift
index 5a21fa7..d915951 100644
--- a/Examples/CustomNavigationTitleExample/CustomNavigationTitleExample/ContentView.swift
+++ b/Examples/CustomNavigationTitleExample/CustomNavigationTitleExample/ContentView.swift
@@ -13,7 +13,7 @@ struct ContentView: View {
NavigationStack{
List {
Section{
- VStack(spacing: 10){
+ VStack(alignment: .leading, spacing: 10){
Image(systemName: "hand.raised.app.fill")
.resizable()
.scaledToFit()
@@ -24,12 +24,11 @@ struct ContentView: View {
.bold()
.titleVisibilityAnchor()
Text("Control which apps can access your data, location, camera, and microphone, and manage safety protections. [Learn more..](https://www.apple.com).")
- .multilineTextAlignment(.center)
+ .multilineTextAlignment(.leading)
}
- .padding(22)
- .frame(maxWidth: .infinity, alignment: .center)
- .overlay(alignment: .topLeading){
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .overlay(alignment: .top){
Text("This is Demo")
.fontWeight(.heavy)
.foregroundStyle(.red)
@@ -43,7 +42,7 @@ struct ContentView: View {
}
}
#if canImport(UIKit)
- .listStyle(.insetGrouped)
+ .listStyle(.automatic)
#endif
.scrollAwareTitle("Privacy & Security")
.navigationTitle("Hello!")
diff --git a/README-ja.md b/README-ja.md
index f60b649..111f925 100644
--- a/README-ja.md
+++ b/README-ja.md
@@ -6,7 +6,9 @@
CustomNavigationTitleは、スクロールに応じてナビゲーションタイトルをアニメーション付きで表示・非表示にするSwiftUI用のシンプルなパッケージです。(この効果はApple純正の設定アプリ、メールアプリ、AppStoreアプリで使われています。)
-
+| | |
+|---|---|
+|
|
|
## 特徴
- スクロールに応じてタイトルがフェードイン・アウト
diff --git a/README.md b/README.md
index 00505c5..a4dda60 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,9 @@
CustomNavigationTitle is a simple SwiftUI package that animates the display and hiding of the navigation title based on scrolling. (This effect is used in Apple's native Settings, Mail, and App Store apps.)
-
+| | |
+|---|---|
+|
|
|
## Features
- The title fades in and out based on scrolling.
diff --git a/Sources/CustomNavigationTitle/CustomNavigationTitle.swift b/Sources/CustomNavigationTitle/CustomNavigationTitle.swift
index d1ae290..02d5ded 100644
--- a/Sources/CustomNavigationTitle/CustomNavigationTitle.swift
+++ b/Sources/CustomNavigationTitle/CustomNavigationTitle.swift
@@ -20,10 +20,20 @@ extension View {
}
}
-private struct ScrollAwareTitleModifier: ViewModifier {
+struct ScrollAwareTitleModifier: ViewModifier {
@State private var isShowNavigationTitle = false
@Environment(\.scrollAwareTitleAnimation) private var animation
+
let title: V
+ let preferredSolariumEffect: Bool
+ var useSolariumEffect: Bool {
+ #if os(iOS)
+ if #available(iOS 26.0, *) {
+ return preferredSolariumEffect
+ }
+ #endif
+ return false
+ }
func body(content: Content) -> some View {
content
@@ -43,12 +53,14 @@ private struct ScrollAwareTitleModifier: ViewModifier {
.toolbar {
ToolbarItem(placement: .principal) {
title
- .bold()
+ .font(titleFont)
.lineLimit(1)
.opacity(isShowNavigationTitle ? 1 : 0)
- .animation(animation, value: isShowNavigationTitle)
+ .blur(radius: preferredSolariumEffect ? (isShowNavigationTitle ? 0 : 4) : 0)
+ .offset(y: preferredSolariumEffect ? (isShowNavigationTitle ? 0 : 15) : 0)
+ .animation(resolvedAnimation, value: isShowNavigationTitle)
}
-#if compiler(>=6.2)
+ #if compiler(>=6.2)
.modifier{
if #available(iOS 26.0, macOS 26.0, macCatalyst 26.0, *) {
$0.sharedBackgroundVisibility(.hidden)
@@ -56,33 +68,61 @@ private struct ScrollAwareTitleModifier: ViewModifier {
$0
}
}
-#endif
+ #endif
}
-#if canImport(UIKit)
+ #if canImport(UIKit)
.navigationBarTitleDisplayMode(.inline)
-#endif
+ #endif
+ }
+
+ private var titleFont: Font {
+ if #available(iOS 26.0, macOS 26.0, macCatalyst 26.0, *) {
+ // UICTFontTextStyleHeadline, weight: .semibold
+ Font.headline.weight(.semibold)
+ } else {
+ // UICTFontTextStyleShortEmphasizedBody
+ Font.body.bold().leading(.tight)
+ }
+ }
+
+ private var resolvedAnimation: Animation {
+ if preferredSolariumEffect, animation == nil {
+ let u = 0.5 // min(abs(scrollVelocity.dy) / 2, 1)
+ let durationScale = 1 - 0.5 * u
+
+ return isShowNavigationTitle
+ ? .spring(duration: 0.45 * durationScale, bounce: 0.4 * u)
+ : .spring(duration: 0.70 * durationScale, bounce: 0.4 * u)
+ }
+ return animation ?? .easeIn(duration: 0.15)
}
}
+
+
+
extension View {
public func scrollAwareTitle(@ViewBuilder _ title: () -> V) -> some View {
- modifier(ScrollAwareTitleModifier(title: title()))
+ modifier(ScrollAwareTitleModifier(title: title(), preferredSolariumEffect: false))
+ }
+ internal func _scrollAwareTitle(@ViewBuilder _ title: () -> V) -> some View {
+ modifier(ScrollAwareTitleModifier(title: title(), preferredSolariumEffect: true))
}
}
extension View {
@_disfavoredOverload public func scrollAwareTitle(_ title: S) -> some View {
- scrollAwareTitle{
+ _scrollAwareTitle{
Text(title)
}
}
public func scrollAwareTitle(_ title: LocalizedStringKey) -> some View {
- scrollAwareTitle{
+ _scrollAwareTitle{
Text(title)
}
}
@_disfavoredOverload public func scrollAwareTitle(_ title: LocalizedStringResource) -> some View {
- scrollAwareTitle{
+ _scrollAwareTitle{
Text(title)
}
}
diff --git a/Sources/CustomNavigationTitle/ScrollAwareTitleAnimation.swift b/Sources/CustomNavigationTitle/ScrollAwareTitleAnimation.swift
index 3b6c967..481b474 100644
--- a/Sources/CustomNavigationTitle/ScrollAwareTitleAnimation.swift
+++ b/Sources/CustomNavigationTitle/ScrollAwareTitleAnimation.swift
@@ -1,11 +1,11 @@
import SwiftUI
extension EnvironmentValues {
- @Entry var scrollAwareTitleAnimation: Animation = .easeIn(duration: 0.15)
+ @Entry var scrollAwareTitleAnimation: Animation? = nil
}
extension View {
- public func scrollAwareTitleAnimation(_ animation: Animation) -> some View {
+ public func scrollAwareTitleAnimation(_ animation: Animation?) -> some View {
environment(\.scrollAwareTitleAnimation, animation)
}
}