精通 SwiftUI - iOS 17 版

第 50 章
使用 SwiftUI 動畫捲動視圖

在前面的章節中,我們介紹了 iOS 17 中的一項新功能 ScrollView,它可以讓開發者輕鬆檢測滾動位置並實現向上(或向下)滾動的功能。 除了這個功能之外,最新版本的 SwiftUI 還引入了一個名為scrollTransition 的新修飾器,它允許我們觀察視圖的轉換並套用各種動畫效果。

之前,我們建立了一個基本的滾動視圖。 讓我們繼續使用它作為範例。 作為參考,以下是建立滾動視圖的程式碼:

ScrollView {
    LazyVStack(spacing: 10) {
        ForEach(0...50, id: \.self) { index in

            bgColors[index % 5]
                .frame(height: 100)
                .overlay {
                    Text("\(index)")
                        .foregroundStyle(.white)
                        .font(.system(.title, weight: .bold))
                }
                .onTapGesture {
                    withAnimation {
                        scrollID = 0
                    }
                }
        }
    }
    .scrollTargetLayout()
}
.contentMargins(10.0, for: .scrollContent)
.scrollPosition(id: $scrollID)

使用 ScrollTransition 修飾器

滾動視圖中的轉換描述了子視圖出現或消失時應經歷的變化。 新的scrollTransition修飾器使我們能夠監視這些過渡並相應地應用不同的視覺和動畫效果。

圖 50.1. 使用 ScrollTransition 修飾器
圖 50.1. 使用 ScrollTransition 修飾器

為了示範它是如何運作的,讓我們修改上一節中的程式碼,將 scrollTransition 修飾器新增到顏色視圖。 這是更新後的程式碼:

bgColors[index % 5]
    .frame(height: 100)
  .overlay {
      Text("\(index)")
          .foregroundStyle(.white)
          .font(.system(.title, weight: .bold))
  }
  .scrollTransition { content, phase in
      content
          .opacity(phase.isIdentity ? 1.0 : 0.3)
          .scaleEffect(phase.isIdentity ? 1.0 : 0.3)
  }

我們透過更改子視圖的不透明度和大小來建立微動畫。 scrollTransition 閉包傳遞兩個參數:子視圖和過渡階段。 過渡階段有三個可能的值:「.identity」、「.topLeading」和「.bottomTrailing」。 根據階段,我們可以應用不同的視覺效果。

.identity 值表示子視圖在捲動視圖的可見區域中完全可見。 .topLeading 值表示視圖即將移動到滾動視圖頂部邊緣的可見區域,而 .bottomTrailing 表示視圖即將移動到滾動視圖底部邊緣的可見區域。

在「identity」階段,滾動轉換通常不應導致視圖發生任何視覺變化。 因此,在上面的程式碼中,當視圖處於「identity」階段時,我們將不透明度和大小重設為原始狀態。 對於其他階段,我們使視圖更小、更透明。 這就是我們在滾動過渡期間為視圖設定動畫的方式。

使用滾動轉換配置

滾動過渡配置控制視圖出現或消失時如何過渡。 當您使用.scrollTransition修飾器時,預設配置是.interactive。 當你將視圖捲動到容器的可見區域時,此配置可讓你平滑地混合過渡效果。

圖 50.2. 使用預設配置進行滾動過渡
圖 50.2. 使用預設配置進行滾動過渡

除了預設配置之外,你還可以選擇使用 .animated 在顯示視圖時平滑地設定過渡動畫。 你可以像這樣取代.scrollTransition修飾器來實現稍微不同的動畫效果:

.scrollTransition(.animated) { content, phase in

    content
        .opacity(phase.isIdentity ? 1.0 : 0.3)
        .scaleEffect(phase.isIdentity ? 1.0 : 0.3)

}

另外,你也可以選擇定義過渡動畫的閾值。 讓我舉個例子來說明為什麼我們可能需要調整閾值。 在程式碼中,將顏色視圖的框架高度從 100 修改為 300,如下所示:

bgColors[index % 5]
        .frame(height: 300)

進行更改後,你應該注意到滾動視圖中的第三個項目已最小化,並且已經套用了透明效果。

圖 50.3. 將框架高度更改為 300 點
圖 50.3. 將框架高度更改為 300 點

這不是我們所需的 UI 佈局。 我們期望第三個項目完全可見且不處於過渡狀態。 在這種情況下,我們可以更改閾值來定義動畫發生的時間。

此閾值根據視圖與滾動視圖相交的程度來確定視圖何時被視為可見(即「identity」階段)。 若要變更閾值,您可以像這樣更新 scrollTransition 修飾器:

.scrollTransition(.animated.threshold(.visible(0.3))) { content, phase in

    .
    .
    .

}

上面的程式碼設定了一個閾值,當視圖在滾動區域內可見 30% 時,則認為視圖完全可見。 一旦更新程式碼,滾動視圖的第三項將完全顯示。 僅當項目可見度低於 30% 時才會出現動畫。

圖 50.4. 當項目可見度低於 30% 時,會出現動畫
圖 50.4. 當項目可見度低於 30% 時,會出現動畫

使用相位值(Phase Value)

phase 參數提供過渡階段的值,範圍從-1.0到1.0。 該值可用於套用縮放或其他動畫效果。 當phase為-1時,代表 topLeading 階段。當為1時,代表 bottomTrailing 階段。 identity 階段則以值 0 表示。

圖 50.5. 旋轉效果
圖 50.5. 旋轉效果

例如,我們可以利用相位值來建立三維旋轉效果:

.scrollTransition(.animated.threshold(.visible(0.3))) { content, phase in

    content
        .opacity(phase.isIdentity ? 1.0 : 0.3)
        .scaleEffect(phase.isIdentity ? 1.0 : 0.3)
        .rotation3DEffect(.radians(phase.value), axis: (1, 1, 1))

}

總結

在本教學中,我們解釋了如何在 SwiftUI 中使用 scrollTransition 修飾器來為 ScrollView 中的視圖轉換設定動畫。 此修改器可讓開發人員根據過渡階段將各種視覺和動畫效果應用於子視圖。

透過掌握此修飾器,你可以將App的使用者體驗提升至一個新的水平。

你可以在此處下載演示項目作為參考: