swift - 将复杂的 UIView 与 SwiftUI 集成失败时需要帮助

标签 swift swiftui

我尝试使用“UIViewRepresentable”测试基于 SwiftUI 的运行循环的挥舞动画 UIView,但它似乎根本没有动画。

使用 UIViewRepresentable Protocol 将 swiftui 连接到 UIView。

Swift 用户界面代码:

import SwiftUI

struct WaveView: UIViewRepresentable {
    func makeUIView(context: Context) -> WaveUIView {
        WaveUIView(frame: .init(x: 0, y: 0, width: 300, height: 300))
    }

    func updateUIView(_ view: WaveUIView, context: Context) {
        view.start()
    }
}

struct WaveView_Previews: PreviewProvider {
    static var previews: some View {
        WaveView()
    }
}

我在 UIViewController 上测试的“Waving”UIView 的工作方式。

import Foundation
import UIKit

class WaveUIView:UIView {

    /// wave curvature (default: 1.5)
    open var waveCurvature: CGFloat = 1.5
    /// wave speed (default: 0.6)
    open var waveSpeed: CGFloat = 0.6
    /// wave height (default: 5)
    open var waveHeight: CGFloat = 5
    /// real wave color
    open var realWaveColor: UIColor = UIColor.red {
        didSet {
            self.realWaveLayer.fillColor = self.realWaveColor.cgColor
        }
    }
    /// mask wave color
    open var maskWaveColor: UIColor = UIColor.red {
        didSet {
            self.maskWaveLayer.fillColor = self.maskWaveColor.cgColor
        }
    }
    /// float over View
    open var overView: UIView?

    /// wave timmer
    fileprivate var timer: CADisplayLink?
    /// real aave
    fileprivate var realWaveLayer :CAShapeLayer = CAShapeLayer()
    /// mask wave
    fileprivate var maskWaveLayer :CAShapeLayer = CAShapeLayer()
    /// offset
    fileprivate var offset :CGFloat = 0

    fileprivate var _waveCurvature: CGFloat = 0
    fileprivate var _waveSpeed: CGFloat = 0
    fileprivate var _waveHeight: CGFloat = 0
    fileprivate var _starting: Bool = false
    fileprivate var _stoping: Bool = false

    /**
     Init view

     - parameter frame: view frame

     - returns: view
     */
    override init(frame: CGRect) {
        super.init(frame: frame)

        var frame = self.bounds
        frame.origin.y = frame.size.height
        frame.size.height = 0
        maskWaveLayer.frame = frame
        realWaveLayer.frame = frame
        // test
        self.backgroundColor = UIColor.blue
    }

    /**
     Init view with wave color

     - parameter frame: view frame
     - parameter color: real wave color

     - returns: view
     */
    public convenience init(frame: CGRect, color:UIColor) {
        self.init(frame: frame)

        self.realWaveColor = color
        self.maskWaveColor = color.withAlphaComponent(0.4)

        realWaveLayer.fillColor = self.realWaveColor.cgColor
        maskWaveLayer.fillColor = self.maskWaveColor.cgColor

        self.layer.addSublayer(self.realWaveLayer)
        self.layer.addSublayer(self.maskWaveLayer)
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    /**
     Add over view

     - parameter view: overview
     */
    open func addOverView(_ view: UIView) {
        overView = view
        overView?.center = self.center
        overView?.frame.origin.y = self.frame.height - (overView?.frame.height)!
        self.addSubview(overView!)
    }

    /**
     Start wave
     */
    open func start() {
        if !_starting {
            _stop()
            _starting = true
            _stoping = false
            _waveHeight = 0
            _waveCurvature = 0
            _waveSpeed = 0

            timer = CADisplayLink(target: self, selector: #selector(wave))
            timer?.add(to: RunLoop.current, forMode: RunLoop.Mode.common)
        }
    }

    /**
     Stop wave
     */
    open func _stop(){
        if (timer != nil) {
            timer?.invalidate()
            timer = nil
        }
    }

    open func stop(){
        if !_stoping {
            _starting = false
            _stoping = true
        }
    }

    /**
     Wave animation
     */
    @objc func wave() {
        // when view is not visible
//        if overView?.window == nil {
//            print("not playing cause not visible")
//            return
//        }

        if _starting {
            print("started")
            if _waveHeight < waveHeight {
                _waveHeight = _waveHeight + waveHeight/100.0
                var frame = self.bounds
                frame.origin.y = frame.size.height-_waveHeight
                frame.size.height = _waveHeight
                maskWaveLayer.frame = frame
                realWaveLayer.frame = frame
                _waveCurvature = _waveCurvature + waveCurvature / 100.0
                _waveSpeed = _waveSpeed + waveSpeed / 100.0
            } else {
                _starting = false
            }
        }

        if _stoping {
            if _waveHeight > 0 {
                _waveHeight = _waveHeight - waveHeight/50.0
                var frame = self.bounds
                frame.origin.y = frame.size.height
                frame.size.height = _waveHeight
                maskWaveLayer.frame = frame
                realWaveLayer.frame = frame
                _waveCurvature = _waveCurvature - waveCurvature / 50.0
                _waveSpeed = _waveSpeed - waveSpeed / 50.0
            } else {
                _stoping = false
                _stop()
            }
        }

        offset += _waveSpeed

        let width = frame.width
        let height = CGFloat(_waveHeight)

        let path = CGMutablePath()
        path.move(to: CGPoint(x: 0, y: height))
        var y: CGFloat = 0

        let maskpath = CGMutablePath()
        maskpath.move(to: CGPoint(x: 0, y: height))

        let offset_f = Float(offset * 0.045)
        let waveCurvature_f = Float(0.01 * _waveCurvature)

        for x in 0...Int(width) {
            y = height * CGFloat(sinf( waveCurvature_f * Float(x) + offset_f))
            path.addLine(to: CGPoint(x: CGFloat(x), y: y))
            maskpath.addLine(to: CGPoint(x: CGFloat(x), y: -y))
        }

        if (overView != nil) {
            let centX = self.bounds.size.width/2
            let centY = height * CGFloat(sinf(waveCurvature_f * Float(centX)  + offset_f))
            let center = CGPoint(x: centX , y: centY + self.bounds.size.height - overView!.bounds.size.height/2 - _waveHeight - 1 )
            overView?.center = center
        }

        path.addLine(to: CGPoint(x: width, y: height))
        path.addLine(to: CGPoint(x: 0, y: height))

        path.closeSubpath()
        self.realWaveLayer.path = path

        maskpath.addLine(to: CGPoint(x: width, y: height))
        maskpath.addLine(to: CGPoint(x: 0, y: height))

        maskpath.closeSubpath()
        self.maskWaveLayer.path = maskpath

    }



}

我希望 SwiftUI 能够使 View 具有动画效果,并根据动画正确地使框架/边框发生变化。但它现在根本没有动画。

以下是 UIViewController 的动画 View :

    override func viewWillAppear(_ animated: Bool) {
        cardView.start()
    }

    func viewdidload(){
        let frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width * 0.8, height: view.bounds.height * 0.5)
        cardView = HomeCardView(frame: frame, color: .gray)
        cardView.addOverView(someUIView())
        cardView.realWaveColor = UIColor.white.withAlphaComponent(0.7)
        cardView.maskWaveColor = UIColor.white.withAlphaComponent(0.3)
        cardView.waveSpeed = 1.2
        cardView.waveHeight = 10
        view.addSubview(cardView)

        }

最佳答案

你忘记在更新ui方法中添加Overview

func updateUIView(_ view: WaveUIView, context: Context) {
    let overView: UIView = UIView(frame: CGRect.init(x: 0, y: 0, width: 300, height: 300))
    overView.backgroundColor = UIColor.green
    view.addOverView(overView)
    view.start()
}

关于swift - 将复杂的 UIView 与 SwiftUI 集成失败时需要帮助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58388490/

相关文章:

ios - 应用程序从后台返回后,UITabBarController selectedIndex 未更新

swift - 如何防止用户点击按钮两次?解析 Xcode Swift

Xcode 12.3 在启动 SwiftUI 项目时卡住

ios - 如何观察 UserDefaults 的变化?

swift - 无法将类型 'MenuView' 的值分配给类型 'some View'

swift - 如何在 SwiftUI 中扩大列表范围?

ios - 如何在 UICollectionViewCell 中使用详细信息披露按钮?

iphone - 无法使用 AVAudioEngine 播放声音

ios - AlamoFire:公钥固定不起作用

swift - 如何使用 OnTapGesture 事件播放我的乐透动画?