美文网首页
iOS 小组件动画(Gif或者视频)

iOS 小组件动画(Gif或者视频)

作者: 新生的光明 | 来源:发表于2024-09-08 17:52 被阅读0次
    Local.gif

    小组件实现动画的若干种办法:
    1.getTimeline:刷新不及时、且每天有固定的刷新次数
    2.特殊字体:利用Text定时器属性及图片制作成的特殊字体
    3.利用私有方法:clockHandRotationEffect

    一. TimeLine

    网上有很多资料,省略

    二. 特殊字体

    项目地址:https://github.com/liudhzhyym/WidgetAnimationSample?tab=readme-ov-file

    三. clockHandRotationEffect

    这个可以制作很多动画:平移、旋转、缩放等
    具体效果:滚动相册、摇摇乐、时钟、风扇等

    今天我们来细说如何展示Gif或者视频
    gif解析出每一张图片、视频解析出妹一帧

    核心代码如下:

    import SwiftUI
    import ClockHandRotationKit
    
    
    @available(iOS 14.0, *)
    struct ArcView: Shape {
        var arcStartAngle: Double
        var arcEndAngle: Double
        var arcRadius: Double
        func path(in rect: CGRect) -> Path {
            var path = Path()
            path.addArc(center: CGPoint(x: rect.midX, y: rect.midY),
                        radius: arcRadius,
                        startAngle: .degrees(arcStartAngle),
                        endAngle: .degrees(arcEndAngle),
                        clockwise: false)
            return path
        }
    }
    
    @available(iOS 14.0, *)
    //原理其实就是有N帧就放N张图片在原地不动,然后借助mask这个方法放一个遮罩.每个遮罩对应圆的一块角度区域.然后都旋转起来后,就会出现类似电影一样一帧一帧出现的视频效果.
    struct LocalGifView: View {
        //    var entry: DynamicWidgetProvider.Entry
            var name: String
            var body: some View {
                if let gifPath = Bundle.main.path(forResource: name, ofType: "gif"),
                   let gifData = NSData(contentsOfFile: gifPath),
                   let gifImage = UIImage.sd_image(withGIFData: gifData as Data),
                   let gifImages = gifImage.images, gifImages.count > 0 {
                    GeometryReader { proxy in
                        let width = proxy.size.width
                        let height = proxy.size.height
                        let duration = gifImage.duration
                        
                        //https://max2d.com/archives/897   计算原理.   如果要方便,那就是lineWidth设置为imageWH.然后arcRadius设置为一个非常非常大的数即可不用做以下运算. 下面的只是为了说清楚算法
                        //多少度
                        let angle = 360.0 / Double(gifImages.count)
                        //正方形宽高
                        let imageWH = max(width, height)
                        //一半宽高
                        let halfWH = (imageWH / 2)
                        //一半弧度
                        let halfRadian = (angle / 2)/180.0 * M_PI
                        let tanHalf = tan(halfRadian)
                        //半径
                        let radiu = sqrt((5 * halfWH * halfWH) + (halfWH * halfWH) / (tanHalf * tanHalf) + (4 * halfWH * halfWH / tanHalf))
                        let b = halfWH / tanHalf
                        let t = radiu - imageWH - b
                        let lineWidth = radiu - b;
                        let arcRadius = (radiu - (lineWidth / 2)) * 300//这里其实不乘以300才是完完全全正确的结果,每个大小都是刚刚好的.但是这样转起来之后重叠部分会留阴影, 所以加以放大后.半径变大了,但是重叠部分还是那么大.相应的周长绝对速度就变大了. 这样阴影就会快速略过,肉眼便无法感知了.越放大效果越好.应该是这个理
                        let offsetY = arcRadius - t / 2
                        ZStack {
                            ForEach(1...gifImages.count, id: \.self) { index in
                                Image(uiImage: gifImages[gifImages.count-index])
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: width, height: height)
                                    .mask(
                                        ArcView(arcStartAngle: angle * Double(index - 1),
                                                arcEndAngle: angle * Double(index),
                                                arcRadius: arcRadius)
                                        .stroke(style: .init(lineWidth: lineWidth, lineCap: .square, lineJoin: .miter))
                                        .frame(width: width, height: height)
                                        .clockHandRotationEffect(period: .custom(duration))
                                        .offset(y: offsetY)
                                    )
                            }
                        }
                        .frame(width: width, height: height)
                    }
                }
            }
    }
    
    

    第二种写法:

    import SwiftUI
    import ClockHandRotationKit
    import SDWebImage
    
    struct ArcView: Shape {
        var arcStartAngle: Double
        var arcEndAngle: Double
        var arcRadius: Double
        func path(in rect: CGRect) -> Path {
            var path = Path()
            path.addArc(center: CGPoint(x: rect.midX, y: rect.midY),
                        radius: arcRadius,
                        startAngle: .degrees(arcStartAngle),
                        endAngle: .degrees(arcEndAngle),
                        clockwise: false)
            return path
        }
    }
    
    struct DynamicGifView: View {
        var entry: DynamicWidgetProvider.Entry
        var name: String
        var body: some View {
            if let gifPath = Bundle.main.path(forResource: name, ofType: "gif"),
               let gifData = NSData(contentsOfFile: gifPath),
               let gifImage = UIImage.sd_image(withGIFData: gifData as Data),
               let gifImages = gifImage.images, gifImages.count > 0 {
                GeometryReader { proxy in
                    let width = proxy.size.width
                    let height = proxy.size.height
                    let arcWidth = max(width, height)
                    let arcRadius = arcWidth * arcWidth
                    let angle = 360.0 / Double(gifImages.count)
                    let duration = gifImage.duration
                    ZStack {
                        ForEach(1...gifImages.count, id: \.self) { index in
                            Image(uiImage: gifImages[index-1])
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(width: width, height: height)
                                .mask(
                                    ArcView(arcStartAngle: angle * Double(index - 1),
                                            arcEndAngle: angle * Double(index),
                                            arcRadius: arcRadius)
                                    .stroke(style: .init(lineWidth: arcWidth, lineCap: .square, lineJoin: .miter))
                                    .frame(width: width, height: height)
                                    .clockHandRotationEffect(period: .custom(duration / 2))
                                    .offset(y: arcRadius) // ⚠️ 需要先进行旋转,再设置offset
                                )
                        }
                    }
                    .frame(width: width, height: height)
                }
            }
        }
    }
    
    

    ClockHandRotationKit 用到了私有方法已经被苹果封了,需要制作XCFrameWork引入到工程

    import SwiftUI
    import WidgetKit
    
    
    @available(iOS 14.0, *)
    public enum ClockHandRotationPeriod {
        case custom(TimeInterval)
        case secondHand, hourHand, minuteHand
    }
    
    @available(iOS 14.0, *)
    public struct ClockHandRotationModifier : ViewModifier {
    
        let clockPeriod: WidgetKit._ClockHandRotationEffect.Period
        let clockTimezone: TimeZone
        let clockAnchor: UnitPoint
        
        public init(period: ClockHandRotationPeriod, timezone: TimeZone = .current, anchor: UnitPoint = .center) {
            var clockPeriod: WidgetKit._ClockHandRotationEffect.Period = .secondHand
            switch period {
            case .custom(let timeInterval):
                clockPeriod = .custom(timeInterval)
            case .secondHand:
                clockPeriod = .secondHand
            case .hourHand:
                clockPeriod = .hourHand
            case .minuteHand:
                clockPeriod = .minuteHand
            }
            self.clockPeriod = clockPeriod
            self.clockTimezone = timezone
            self.clockAnchor = anchor
        }
    
        public func body(content: Content) -> some View {
            content
                ._clockHandRotationEffect(self.clockPeriod, in: self.clockTimezone, anchor: self.clockAnchor)
        }
    
    }
    
    @available(iOS 14.0, *)
    extension View {
    
        public func clockHandRotationEffect(period : ClockHandRotationPeriod, in timeZone: TimeZone = .current, anchor: UnitPoint = .center) -> some View {
            return modifier(ClockHandRotationModifier(period: period, timezone: timeZone, anchor: anchor))
    
            
        }
        
       
    }
    

    参考资料:
    特殊GIF字体
    TopWidget开源代码
    Gif动画实现
    灵动组件

    相关文章

      网友评论

          本文标题:iOS 小组件动画(Gif或者视频)

          本文链接:https://www.haomeiwen.com/subject/cskeljtx.html