SwiftUI 列表动画

Posted

技术标签:

【中文标题】SwiftUI 列表动画【英文标题】:SwiftUI list animations 【发布时间】:2020-04-19 20:48:29 【问题描述】:

我正在关注 Apple 的 Swift UI Animating Views And Transitions,我注意到 Hike Graph View 中有一个错误。当我点击图表时,它不允许我从海拔切换到心率或配速。它不允许我,只是退出视图。我认为这与此处的列表有关:

           VStack(alignment: .leading) 
                Text("Recent Hikes")
                    .font(.headline)

                HikeView(hike: hikeData[0])
            

远足视图包含:

import SwiftUI

struct HikeView: View 
    var hike: Hike
    @State private var showDetail = false

    var transition: AnyTransition 
        let insertion = AnyTransition.move(edge: .trailing)
            .combined(with: .opacity)
        let removal = AnyTransition.scale
            .combined(with: .opacity)
        return .asymmetric(insertion: insertion, removal: removal)
    

    var body: some View 
        VStack 
            HStack 
                HikeGraph(hike: hike, path: \.elevation)
                    .frame(width: 50, height: 30)
                    .animation(nil)

                VStack(alignment: .leading) 
                    Text(hike.name)
                        .font(.headline)
                    Text(hike.distanceText)
                

                Spacer()

                Button(action: 
                    withAnimation 
                        self.showDetail.toggle()
                    
                ) 
                    Image(systemName: "chevron.right.circle")
                        .imageScale(.large)
                        .rotationEffect(.degrees(showDetail ? 90 : 0))
                        .scaleEffect(showDetail ? 1.5 : 1)
                        .padding()
                
            

            if showDetail 
                HikeDetail(hike: hike)
                    .transition(transition)
            
        
    

远足细节包含:

struct HikeDetail: View 
    let hike: Hike
    @State var dataToShow = \Hike.Observation.elevation

    var buttons = [
        ("Elevation", \Hike.Observation.elevation),
        ("Heart Rate", \Hike.Observation.heartRate),
        ("Pace", \Hike.Observation.pace),
    ]

    var body: some View 
        return VStack 
            HikeGraph(hike: hike, path: dataToShow)
                .frame(height: 200)

            HStack(spacing: 25) 
                ForEach(buttons, id: \.0)  value in
                    Button(action: 
                        self.dataToShow = value.1
                    ) 
                        Text(value.0)
                            .font(.system(size: 15))
                            .foregroundColor(value.1 == self.dataToShow
                                ? Color.gray
                                : Color.accentColor)
                            .animation(nil)
                    
                
            
        
    

远足 Graoh 包含:


import SwiftUI

func rangeOfRanges<C: Collection>(_ ranges: C) -> Range<Double>
    where C.Element == Range<Double> 
    guard !ranges.isEmpty else  return 0..<0 
    let low = ranges.lazy.map  $0.lowerBound .min()!
    let high = ranges.lazy.map  $0.upperBound .max()!
    return low..<high


func magnitude(of range: Range<Double>) -> Double 
    return range.upperBound - range.lowerBound


extension Animation 
    static func ripple(index: Int) -> Animation 
        Animation.spring(dampingFraction: 0.5)
            .speed(2)
            .delay(0.03 * Double(index))
    


struct HikeGraph: View 
    var hike: Hike
    var path: KeyPath<Hike.Observation, Range<Double>>

    var color: Color 
        switch path 
        case \.elevation:
            return .gray
        case \.heartRate:
            return Color(hue: 0, saturation: 0.5, brightness: 0.7)
        case \.pace:
            return Color(hue: 0.7, saturation: 0.4, brightness: 0.7)
        default:
            return .black
        
    

    var body: some View 
        let data = hike.observations
        let overallRange = rangeOfRanges(data.lazy.map  $0[keyPath: self.path] )
        let maxMagnitude = data.map  magnitude(of: $0[keyPath: path]) .max()!
        let heightRatio = (1 - CGFloat(maxMagnitude / magnitude(of: overallRange))) / 2

        return GeometryReader  proxy in
            HStack(alignment: .bottom, spacing: proxy.size.width / 120) 
                ForEach(data.indices)  index in
                    GraphCapsule(
                        index: index,
                        height: proxy.size.height,
                        range: data[index][keyPath: self.path],
                        overallRange: overallRange)
                    .colorMultiply(self.color)
                    .transition(.slide)
                    .animation(.ripple(index: index))
                
                .offset(x: 0, y: proxy.size.height * heightRatio)
            
        
    

图形胶囊包含:

import SwiftUI

struct GraphCapsule: View 
    var index: Int
    var height: CGFloat
    var range: Range<Double>
    var overallRange: Range<Double>

    var heightRatio: CGFloat 
        max(CGFloat(magnitude(of: range) / magnitude(of: overallRange)), 0.15)
    

    var offsetRatio: CGFloat 
        CGFloat((range.lowerBound - overallRange.lowerBound) / magnitude(of: overallRange))
    

    var body: some View 
        Capsule()
            .fill(Color.white)
            .frame(height: height * heightRatio)
            .offset(x: 0, y: height * -offsetRatio)
    

有没有办法解决这个问题?谢谢

【问题讨论】:

【参考方案1】:

问题可能在 SwiftUI 中更深——如果您在 HikeGraph 中注释掉 transition(.slide)(并重新启动 XCODE),它将开始工作

【讨论】:

以上是关于SwiftUI 列表动画的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:动画列表中的圆圈

SwiftUI 动画列表行的展开和折叠

SwiftUI - 在列表中的视图内触发的动画也不会为列表设置动画

在 SwiftUI 列表中移动项目时不需要的动画

SwiftUI找回丢失的列表视图(List)动画

SwiftUI找回丢失的列表视图(List)动画