You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matched Geometry Effect动画Bug及滚动视图图片居中对齐求助

SwiftUI Matched Geometry Effect 动画问题与布局调整解决方案

问题概述

  • 点击第一张图片时动画不流畅;
  • 点击第二张图片时,弹出的分页ScrollView会同时显示图片1和图片2的一半;
  • 点击第三张图片时动画过程中第一张图片会闪烁;
  • 需要将底部ScrollView的图片居中对齐,且每张图片不占满屏幕宽度。

修复后的完整代码

import SwiftUI
import Kingfisher

struct TopPostMediaView: View {
    @State private var isExpanded: Bool = false
    @Namespace private var animation
    @State private var selectedItemID: UUID? = nil
    @State private var expandedScrollPosition: UUID? = nil
    
    // 自定义缩略图宽度,可根据需求调整
    private let thumbnailWidth: CGFloat = 280
    
    var body: some View {
        VStack(spacing: 0) {
            // 缩略图滚动区域
            ScrollView(.horizontal) {
                HStack(spacing: 16) {
                    ForEach(mynewtemp) { pic in
                        KFImage(URL(string: pic.photo))
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: thumbnailWidth, height: 250)
                            .clipShape(RoundedRectangle(cornerRadius: 10))
                            .contentShape(RoundedRectangle(cornerRadius: 10))
                            .matchedGeometryEffect(id: pic.id, in: animation)
                            .onTapGesture {
                                selectedItemID = pic.id
                                expandedScrollPosition = pic.id
                                withAnimation(.easeInOut(duration: 0.3)) {
                                    isExpanded = true
                                }
                            }
                    }
                }
                // 左右内边距实现居中对齐
                .padding(.horizontal, (UIScreen.main.bounds.width - thumbnailWidth) / 2)
                .scrollTargetLayout()
            }
            .scrollPosition(id: $selectedItemID, anchor: .center)
            .scrollIndicators(.hidden)
            .scrollTargetBehavior(.viewAligned)
            .frame(height: 250)
            
            // 展开后的分页视图
            if isExpanded {
                ZStack(alignment: .topLeading) {
                    Color.black.ignoresSafeArea()
                    
                    ScrollView(.horizontal) {
                        LazyHStack(spacing: 0) {
                            ForEach(mynewtemp) { pic in
                                KFImage(URL(string: pic.photo))
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .containerRelativeFrame(.horizontal)
                                    .clipped()
                                    .matchedGeometryEffect(id: pic.id, in: animation)
                            }
                        }
                        .scrollTargetLayout()
                    }
                    .scrollPosition(id: $expandedScrollPosition)
                    .scrollTargetBehavior(.paging)
                    .scrollIndicators(.hidden)
                    
                    Button(action: {
                        withAnimation(.easeInOut(duration: 0.3)) {
                            isExpanded = false
                        }
                    }, label: {
                        Image(systemName: "xmark.circle.fill")
                            .font(.title)
                            .foregroundColor(.white.opacity(0.8))
                            .padding()
                    })
                }
                .transition(.opacity)
            }
        }
    }
}

#Preview {
    TopPostMediaView()
}

let mynewtemp: [tempStruct] = [
    tempStruct(id: UUID(), photo: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRmCy16nhIbV3pI1qLYHMJKwbH2458oiC9EmA&s"),
    tempStruct(id: UUID(), photo: "https://th.bing.com/th/id/OIG2.9O4YqGf98tiYzjKDvg7L"),
    tempStruct(id: UUID(), photo: "https://th.bing.com/th/id/OIG2.9O4YqGf98tiYzjKDvg7L")
]

struct tempStruct: Identifiable, Hashable {
    var id: UUID
    var photo: String
}

关键修复说明

  1. 统一匹配ID:
    把原代码中拼接字符串的ID改为直接使用pic.id,避免ID重复或无效匹配,彻底解决动画闪烁、错位问题。

  2. 分离滚动状态:
    将原代码复用的detailScrollPosition拆分为selectedItemID(控制缩略图滚动)和expandedScrollPosition(控制展开视图滚动),避免滚动状态冲突导致的视图显示异常。

  3. 实现缩略图居中布局:
    通过给HStack添加动态左右内边距(UIScreen.main.bounds.width - thumbnailWidth) / 2实现居中,同时设置固定缩略图宽度,让图片不占满屏幕。

  4. 优化动画逻辑:
    移除原代码中冗余的透明占位视图,直接通过matchedGeometryEffect处理过渡,减少视图层级和不必要渲染;调整动画时长到0.3秒,让过渡更自然流畅。

  5. 简化布局计算:
    移除手动计算高度的viewHeight()函数,改用ignoresSafeArea()让黑色背景铺满全屏;优化关闭按钮布局,使用自适应padding替代固定值,适配不同设备。

内容的提问来源于stack exchange,提问作者Ahmed Zaidan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 21:57:27