SwiftUI中ScrollView滚动时如何动态更新视图样式?
实现横向ScrollView滚动时中间元素放大及样式优化
要实现你需要的效果,我们需要跟踪横向ScrollView的滚动位置,判断哪个星座元素处于屏幕中间,然后动态应用缩放、边框等样式。以下是修改后的完整代码:
struct SULoveCompatibilityInputView: View, HoroscopesProtocol { @Environment(\.presentationMode) var presentationMode var viewModel = LoveCompatibilityViewModel() @State var horoscopeInfo: [LoveCompatibilityHoroscopeData] = [] let screenWidth = UIScreen.main.bounds.width let itemWidth: CGFloat = 110 let spacing: CGFloat = ((UIScreen.main.bounds.width/2) - 125) // 跟踪当前处于中间的元素ID @State private var centeredItemId: Int? var body: some View { NavigationView { ZStack{ Color(ThemeManager.pageBackgroundColor()).ignoresSafeArea() ScrollView{ VStack{ Spacer().frame(height: 20) UserHoroscopeInfoView() Spacer().frame(height: 45) FriendHoroscopeInfoView().padding() Spacer().frame(height: 5) ScrollViewReader { proxy in ScrollView(.horizontal, showsIndicators: false){ HStack(spacing: spacing){ Spacer().frame(width: 70) ForEach(horoscopeInfo, id: \.id) { signData in HoroscopeItemView(signData: signData) .frame(width: itemWidth) // 监听元素位置,判断是否处于中间区域 .background( GeometryReader { geo in Color.clear .onAppear { checkIfCentered(geo: geo, itemId: signData.id) } .onChange(of: proxy.scrollOffset) { _ in checkIfCentered(geo: geo, itemId: signData.id) } } ) } Spacer().frame(width: 70) } .padding(.top, 10) .padding(.bottom, 15) } } .onAppear{ viewModel.horoscopes = self viewModel.getHoroscopes() } } } .navigationTitle("horoscopeCompatibilityTitle".localized) .navigationBarItems(leading: Button(action: { presentationMode.wrappedValue.dismiss() }) { Image(systemName: "chevron.left") }) // 传递中间元素ID到子视图 .environment(\.centeredItemId, centeredItemId) } } } // 判断元素是否处于屏幕中间区域 private func checkIfCentered(geo: GeometryProxy, itemId: Int) { let itemCenterX = geo.frame(in: .global).midX let screenCenterX = screenWidth / 2 // 中间区域阈值,可按需调整 let threshold: CGFloat = 50 if abs(itemCenterX - screenCenterX) < threshold { centeredItemId = itemId } else if centeredItemId == itemId { centeredItemId = nil } } func fillFriendSignScroll(data: [LoveCompatibilityHoroscopeData]) { horoscopeInfo = data } // 抽离星座元素为独立View,便于样式控制 private struct HoroscopeItemView: View { let signData: LoveCompatibilityHoroscopeData @Environment(\.centeredItemId) private var centeredItemId private var isCentered: Bool { centeredItemId == signData.id } var body: some View { VStack{ Spacer().frame(height: 20) ZStack(alignment: .topTrailing){ Image(signData.signIcon ?? "virgo") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 110, height: 110) // 中间元素放大至1.2倍,添加平滑动画 .scaleEffect(isCentered ? 1.2 : 1.0) .animation(.easeInOut(duration: 0.2), value: isCentered) // 添加带圆角的边框,中间元素边框加粗 .overlay( RoundedRectangle(cornerRadius: 8) .stroke(Color(ThemeManager.accentColor()), lineWidth: isCentered ? 2 : 1) .animation(.easeInOut(duration: 0.2), value: isCentered) ) Image("popupTik") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 20, height: 20) // 添加阴影提升可见性 .shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 1) // 中间元素的勾选图标进一步放大 .scaleEffect(isCentered ? 1.1 : 1.0) .animation(.easeInOut(duration: 0.2), value: isCentered) // 微调位置避免被放大的图片遮挡 .offset(x: isCentered ? 5 : 0, y: isCentered ? -5 : 0) } Spacer().frame(height: 20) Text(signData.signName ?? "Burç") .font(.system(size: 16).bold()) .foregroundColor(Color(ThemeManager.textColor())) Spacer().frame(height: 5) Text(signData.signDateStart ?? "") .font(.system(size: 14, weight: .medium)) .foregroundColor(Color(ThemeManager.textColor())) Spacer().frame(height: 2) Text(signData.signDateEnd ?? "") // 修复原代码重复显示起始日期的问题 .font(.system(size: 14, weight: .medium)) .foregroundColor(Color(ThemeManager.textColor())) } } } } // 定义环境变量传递中间元素ID private struct CenteredItemIdKey: EnvironmentKey { static let defaultValue: Int? = nil } extension EnvironmentValues { var centeredItemId: Int? { get { self[CenteredItemIdKey.self] } set { self[CenteredItemIdKey.self] = newValue } } }
关键修改说明:
- 中间元素识别:通过
GeometryReader监听每个元素的全局位置,对比屏幕中心判断是否处于中间区域,用centeredItemId记录当前中间元素 - 动态缩放效果:给图片添加
scaleEffect,中间元素放大至1.2倍,配合动画实现平滑过渡 - 边框样式优化:用
overlay添加圆角边框,中间元素边框加粗,增强视觉区分度 - 勾选图标增强:增大图标尺寸、添加阴影,中间时进一步放大并微调位置,提升可见性
- 代码重构:将星座元素抽离为独立View,通过环境变量传递状态,代码更清晰易维护
- 细节修复:修正原代码中重复显示起始日期的错误,改为显示结束日期
内容的提问来源于stack exchange,提问作者onur.altintas
相关产品推荐
相关产品推荐

