簡體   English   中英

在iOS中的軸心點周圍旋轉ImageView

[英]Rotate a ImageView around a pivot point in iOS

記錄和旋轉搜索欄

我有一個App屏幕,可以錄制(最多30秒)音頻。

  1. 在錄制音頻的同時,如何在虛線圓圈線上將小圓圈作為搜索條平滑旋轉?
  2. 當小圓圈沿着線旋轉時,如何填充虛線。

在此輸入圖像描述

謝謝。

答案有兩種方法可以使用PanGesture並自動使用Timer

1.使用UIPanGestureRecognizer:

您可以使用UIPanGestureRecognizer實現此UIPanGestureRecognizer circleView是你mainViewnob是另一種viewimageView將外移到circleView

panGesture = UIPanGestureRecognizer(target: self, action: #selector(panHandler(_:)))
nob.addGestureRecognizer(panGesture)

panHandler(_ :)定義panHandler(_ :)

@objc func panHandler(_ gesture: UIPanGestureRecognizer) {
    let point = gesture.location(in: self)
    updateForPoints(point)
}

以下是核心邏輯如何運作。

func updateForPoints(_ point: CGPoint) {

    /*
     * Parametric equation of circle
     * x = a + r cos t
     * y = b + r sin ⁡t
     * a, b are center of circle
     * t (theta) is angle
     * x and y will be points which are on circumference of circle
     *
               270
                |
            _   |   _
                |
     180 -------o------- 360
                |
            +   |   +
                |
               90
     *
     */

    let centerOffset =  CGPoint(x: point.x - circleView.frame.midX, y: point.y - circleView.frame.midY)

    let a: CGFloat = circleView.center.x
    let b: CGFloat = circleView.center.y
    let r: CGFloat = circleView.layer.cornerRadius - 2.5
    let theta: CGFloat = atan2(centerOffset.y, centerOffset.x)
    let newPoints = CGPoint(x: a + r * cos(theta), y: b + r * sin(theta))

    var rect = nob.frame
    rect.origin = newPoints
    nob.center = newPoints
}

2.使用Timer自動移動

let totalSeconds: Int = 30 // You can change it whatever you want
var currentSecond: Int = 1
var timer: Timer?


func degreesToRadians(_ degree: CGFloat) -> CGFloat {
     /// Will convert the degree (180°) to radians (3.14)
     return degree * .pi / 180
}

func angleFromSeconds(_ seconds: Int) -> CGFloat {
     /// Will calculate the angle for given seconds
     let aSliceAngle = 360.0 / CGFloat(totalSeconds)
     let angle = aSliceAngle * CGFloat(seconds) - 90
     return angle
}

/// ------------- TIMER METHODS ------------- ///

func startTimer() {
     stopTimer()
     timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timeDidUpdate(_ :)), userInfo: nil, repeats: true)
     timeDidUpdate(timer!)
}

func stopTimer() {
     currentSecond = 1
     timer?.invalidate()
     timer = nil
}

@objc func timeDidUpdate(_ t: Timer) {
      let angle = angleFromSeconds(currentSecond)
      let theta = degreesToRadians(angle)
      updateAngle(theta: theta, animated: true)
      currentSecond += 1

      if currentSecond > totalSeconds {
         self.stopTimer()
      }
}

/// --------------- MAIN METHOD -------------- ///
func updateAngle(theta: CGFloat, animated: Bool) {

     let a: CGFloat = circleView.center.x
     let b: CGFloat = circleView.center.y
     let r: CGFloat = circleView.layer.cornerRadius
     let newPoints = CGPoint(x: a + r * cos(theta), y: b + r * sin(theta))

     var rect = nob.frame
     rect.origin = newPoints

     if animated {
         UIView.animate(withDuration: 0.1, animations: {
         self.nob.center = newPoints

         /// Uncomment if your nob is not a circle and you want to maintain the angle too
         // self.nob.transform = CGAffineTransform.identity.rotated(by: theta)
         })
     }
     else {
         nob.center = newPoints

         /// Uncomment if your nob is not a circle and you want to maintain the angle too
         //nob.transform = CGAffineTransform.identity.rotated(by: theta)
     }
}

如果我理解正確,你想要有一個很好的動畫進度指示器。 當然有很多方法可以實現這一目標。 我將為您提供一些復雜的解決方案,讓您完全掌控動畫 - 動畫期間的變化速度,從不同點開始,隨時暫停,甚至還原動畫。

1)讓我們從完整的工作示例開始。 我們需要很少的屬性:

class ViewController: UIViewController {

   var displayLink:CADisplayLink?
   var circlePathLayer = CAShapeLayer()
   var dottedLine = CAShapeLayer()
   var beginTime:TimeInterval?

2)定義顯示鏈接。 它是非常快的觸發器,每個顯示器上的發送事件刷新 - 即每秒60次,可以手動設置,此進度函數將處理進度視圖的正確狀態

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .blue
    beginTime = Date().timeIntervalSinceReferenceDate
    displayLink = CADisplayLink(target: self, selector: #selector(progress))
    displayLink?.add(to: RunLoop.main, forMode: .defaultRunLoopMode)

3)定義你的循環路徑,應該遵循什么

let path = UIBezierPath(arcCenter: view.center, radius: view.center.x - 20, startAngle: -CGFloat.pi / 2, endAngle: CGFloat.pi * 2 - CGFloat.pi / 2, clockwise: true)

4)定義線的虛線和默認動畫。 在CAMediaTimming協議之后有timeOffsetspeedbeginTime屬性,我們不想動畫任何東西,並將該層的繪圖設置為零開始狀態

        dottedLine.timeOffset = 0
        dottedLine.speed = 0
        dottedLine.duration = 1
        dottedLine.beginTime = dottedLine.convertTime(CACurrentMediaTime(), from: nil)
        dottedLine.repeatCount = 1
        dottedLine.autoreverses = false

        dottedLine.fillColor = nil
        dottedLine.fillMode = kCAFillModeBoth
        dottedLine.strokeStart = 0.0
        dottedLine.strokeColor = UIColor.white.cgColor
        dottedLine.lineWidth = 5.0
        dottedLine.lineJoin = kCALineJoinMiter
        dottedLine.lineDashPattern = [10,10]
        dottedLine.lineDashPhase = 3.0
        dottedLine.path = path.cgPath

        let pathAnimation = CAKeyframeAnimation(keyPath: "strokeEnd")
        pathAnimation.duration = 1
        pathAnimation.isRemovedOnCompletion = false
        pathAnimation.autoreverses = true
        pathAnimation.values = [0, 1]
        pathAnimation.fillMode = kCAFillModeBoth
        dottedLine.add(pathAnimation, forKey: "strokeEnd")
        view.layer.addSublayer(dottedLine)

5)小圓相同

        let circlePath = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0), radius: 10, startAngle: 0, endAngle:CGFloat.pi * 2, clockwise: true)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.strokeColor = nil

        circlePathLayer.addSublayer(shapeLayer)
        circlePathLayer.timeOffset = 0
        circlePathLayer.speed = 0
        circlePathLayer.beginTime = circlePathLayer.convertTime(CACurrentMediaTime(), from: nil)
        circlePathLayer.duration = 1
        circlePathLayer.repeatCount = 1
        circlePathLayer.autoreverses = false
        circlePathLayer.fillColor = nil
        view.layer.addSublayer(circlePathLayer)

        let circleAnimation = CAKeyframeAnimation(keyPath: "position")
        circleAnimation.duration = 1
        circleAnimation.isRemovedOnCompletion = false
        circleAnimation.autoreverses = false
        circleAnimation.values = [0, 1]
        circleAnimation.fillMode = kCAFillModeBoth
        circleAnimation.path = path.cgPath
        circlePathLayer.add(circleAnimation, forKey: "position")        
}

6)最后進度函數,它經常調用,此時你要設置進度位置 - 在我的例子中設置為30秒,但你可以在這里添加一些條件,不要改變時間偏移 - 暫停,或改變它有不同的處理速度,或放回去。

 @objc func progress() {
        let time = Date().timeIntervalSinceReferenceDate - (beginTime ?? 0)
        circlePathLayer.timeOffset = time / 30
        dottedLine.timeOffset = time / 30
    }

7)一旦完成動畫,不要忘記釋放資源並使顯示鏈接無效

displayLink?.invalidate()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM