Поверните collectionView по кругу, следуя указаниям пользователя - PullRequest
0 голосов
/ 01 февраля 2019

Я пытаюсь создать collectionView с компоновкой контроллера, и я хочу, чтобы collectionView вращался по кругу, когда пользователь проводил пальцем по экрану в любом направлении.Я нашел макет круга для коллекции. Вот что я сделал до сих пор

enter image description here

, чтобы повернуть эту коллекцию. Я написал этот код

добавить жест в collectionView

panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.gestureReader(_:)))
    panGesture.cancelsTouchesInView = false
    self.collectionView.addGestureRecognizer(panGesture)

вот методы gestReader и анимации

@objc private func gestureReader(_ gesture: UIPanGestureRecognizer) {
    var startLocation = CGPoint.zero
    var endLocation = CGPoint.zero
    let currentLocation = gesture.location(in: self.collectionView)

    if gesture.state == .began {
        startLocation = currentLocation
    }

    if gesture.state == .ended {
        endLocation = currentLocation
        self.startRotatingView(start: startLocation, end: endLocation)
    }
}

private func startRotatingView(start:CGPoint, end: CGPoint) {
    let dx = end.x - start.x
    let dy = end.y - start.y

    let distance = abs(sqrt(dx*dx + dy*dy))
    print(distance)


    if start.x > end.x {
        if start.y > end.y {
            //positive value of pi
            self.circleAnimation(-distance)
        }else {
            //negitive value of pi
            self.circleAnimation(distance)
        }
    }else {
        if start.y > end.y {
            //positive value of pi
            self.circleAnimation(-distance)
        }else {
            //negitive value of pi
            self.circleAnimation(distance)
        }
    }
}

private func circleAnimation(_ angle:CGFloat) {
    UIView.animate(withDuration: 0.7, delay: 0,  options: .curveLinear, animations: {
        self.collectionView.transform = CGAffineTransform.identity
        self.collectionView.transform = CGAffineTransform.init(rotationAngle: angle)
    }) { (true) in
        //
    }
}

Во-первых, анимация не работает должным образом, а во-вторых, когда collectionView поворачивается, это то, что я получаю

enter image description here

Вопрос 1: Что еще мне нужно добавить, чтобы анимация была плавной и следовала за пальцем пользователя?Вопрос 2: Я хочу, чтобы коллекция Viewcells оставалась такой же, как и до анимации. Как этого добиться, помогите

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Я покажу вам пример здесь.Представление декора S1View является подклассом UICollectionViewCell с идентификатором «background».

Код не сложный для понимания, но сложный для составления.Как управлять аниматором это другая история.

        class TestCollectionViewLayout: UICollectionViewLayout {

            lazy var dataSource : UICollectionViewDataSource? = {
                self.collectionView?.dataSource
            }()

            var layouts : [IndexPath: UICollectionViewLayoutAttributes?] = [:]

            var itemNumber : Int  {
                return   dataSource!.collectionView(collectionView!, numberOfItemsInSection: 0)
            }

            override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?{
                var itemArray = (0..<itemNumber).map{ self.layoutAttributesForItem(at: IndexPath.init(row: $0, section: 0))!}
                itemArray.append(self.layoutAttributesForDecorationView(ofKind:"background"
                    , at: IndexPath.init(row: 0, section: 0)))
                return itemArray
            }

            override var collectionViewContentSize: CGSize { get{
                return  self.collectionView?.frame.size ?? CGSize.zero
                }
            }

            lazy var  dynamicAnimator = {UIDynamicAnimator(collectionViewLayout: self)}()

            private func updateCurrentLayoutAttributesForItem(at indexPath: IndexPath, current: UICollectionViewLayoutAttributes?) -> UICollectionViewLayoutAttributes?{
                return current
            }

            private func initLayoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?{
                let layoutAttributes =   UICollectionViewLayoutAttributes(forCellWith: indexPath)
                let center = (collectionView?.center)!
                let angle = (CGFloat(indexPath.row)  /  CGFloat(itemNumber) * CGFloat.pi * 2)
                layoutAttributes.center = CGPoint.init(x:  center.x + cos(angle) * CGFloat(radius)   , y: center.y + sin(angle) * CGFloat(radius) )
                layoutAttributes.bounds  = CGRect.init(x: 0, y: 0, width: 100, height: 100 )

                if let decorator = self.decorator {
                    let itemBehavior =
                        UIAttachmentBehavior.pinAttachment(with: layoutAttributes, attachedTo: decorator, attachmentAnchor: layoutAttributes.center)
                    dynamicAnimator.addBehavior(itemBehavior)
                    layouts[indexPath] = layoutAttributes
                }
                return layoutAttributes
            }

            override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?{
                guard let currentLayout = layouts[indexPath] else {
                    return initLayoutAttributesForItem(at:indexPath)}
                return currentLayout
            }


            private let radius = 200

            private var decorator: UICollectionViewLayoutAttributes?

            override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes{
                guard let decorator = self.decorator else {
                    let layoutAttributes =   UICollectionViewLayoutAttributes.init(forDecorationViewOfKind: elementKind, with: indexPath)
                    layoutAttributes.center = (self.collectionView?.center)!
                    layoutAttributes.bounds = CGRect.init(x: 0, y: 0, width: radius, height: radius)
                    self.decorator = layoutAttributes
                    return layoutAttributes
                }
                return decorator
            }


            lazy var s: UIDynamicItemBehavior = {
             let decorator = self.decorator!
             let s =   UIDynamicItemBehavior.init(items: [decorator])
             s.angularResistance = 1
             dynamicAnimator.addBehavior(s)
                return s
            }()


            func rotate(_ speed: CGFloat){
                   guard let decorator = self.decorator else {return}
                s.addAngularVelocity(speed, for: decorator)
            }

        }



    class TestCollectionViewController: UICollectionViewController {


        var startLocation = CGPoint.zero
        var endLocation = CGPoint.zero

        @objc private func gestureReader(_ gesture: UIPanGestureRecognizer) {

            let currentLocation = gesture.location(in: self.collectionView)

            if gesture.state == .began {
                startLocation = currentLocation
            }

           else  if gesture.state == .ended {
                endLocation = currentLocation
                self.startRotatingView(start: startLocation, end: endLocation)
            }
        }

        private func startRotatingView(start:CGPoint, end: CGPoint) {
            let dx = end.x - start.x
            let dy = end.y - start.y

            let distance = abs(sqrt(dx*dx + dy*dy))




            if start.x < end.x {
                if start.y > end.y {
                    //positive value of pi
                    self.circleAnimation(-distance)
                }else {
                    //negitive value of pi
                    self.circleAnimation(distance)
                }
            }else {
                if start.y > end.y {
                    //positive value of pi
                    self.circleAnimation(-distance)
                }else {
                    //negitive value of pi
                    self.circleAnimation(distance)
                }
            }
        }

        private func circleAnimation(_ angle:CGFloat) {

            (collectionView.collectionViewLayout as? TestCollectionViewLayout).map{
                $0.rotate(angle / 100)
            }
  //                UIView.animate(withDuration: 0.7, delay: 0,  options: .curveLinear, animations: {
  //                    self.collectionView.transform = CGAffineTransform.identity
  //                    self.collectionView.transform = CGAffineTransform.init(rotationAngle: angle)
  //                }) { (true) in
  //                    //
 //                }
        }



        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
        //                Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { (Timer) in
       //                    self.rotate()
  //                }
        }

        override func viewDidLoad() {


            super.viewDidLoad()
            collectionView.collectionViewLayout = TestCollectionViewLayout()
            collectionView.collectionViewLayout.register(UINib.init(nibName: "S1View", bundle: nil) , forDecorationViewOfKind: "background")

         let   panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.gestureReader(_:)))
            panGesture.cancelsTouchesInView = false
            self.collectionView.addGestureRecognizer(panGesture)

        }

        var data: [Int] = [1,2,3,4,5,6,7]
        override func numberOfSections(in collectionView: UICollectionView) -> Int {
            return 1
        }

        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return data.count
        }

        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)

            return cell
      }
    }

enter image description here

0 голосов
/ 01 февраля 2019

Может быть, этот учебник поможет: https://www.raywenderlich.com/1702-uicollectionview-custom-layout-tutorial-a-spinning-wheel

Ваша первая проблема заключается в том, что вы поворачиваете представление всей коллекции.Думайте об этом, как будто вы помещаете эти круги на лист бумаги, а затем вращаете этот лист бумаги.Вы не хотите вращать представление всей коллекции.Возможно, вы не захотите вращать окружности вокруг точки, потому что тогда вращение влияет на изображение и текст в окружности.Вы просто хотите изменить положение круга в круговом движении.

Если UICollectionView не работает, вы можете отказаться от него и использовать обычные UIViews и расположить их по кругу (эти функции должны помочь: https://gist.github.com/akhilcb/8d03f1f88f87e996aec24748bdf0ce78). После того, как у вас есть представления, расположенные по кругу, вам просто нужно обновить угол для каждого вида, когда пользователь перетаскивает палец. Сохраните предыдущий угол на виде и добавьте к нему все, что вы хотите, когда пользователь перетаскиваетНемного проб и ошибок, и это не должно быть слишком плохо.

Обновление

Основная причина использования представлений коллекции - если у вас многоэлементов, и вам нужно повторно использовать представления в виде списка. Если вам не нужно повторно использовать представления, то использование UICollectionView может быть затруднительно для понимания, настройки и изменения вещей. Вот простой пример использования обычных представлений, которые вращаются вокруг кругаиспользуя вход UIPanGestureRecognizer.

Пример :

import UIKit

class ViewController: UIViewController {

  var rotatingViews = [RotatingView]()
  let numberOfViews = 8
  var circle = Circle(center: CGPoint(x: 200, y: 200), radius: 100)
  var prevLocation = CGPoint.zero

  override func viewDidLoad() {
    super.viewDidLoad()

    for i in 0...numberOfViews {
      let angleBetweenViews = (2 * Double.pi) / Double(numberOfViews)
      let viewOnCircle = RotatingView(circle: circle, angle: CGFloat(Double(i) * angleBetweenViews))
      rotatingViews.append(viewOnCircle)
      view.addSubview(viewOnCircle)
    }

    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(panGesture:)))
    view.addGestureRecognizer(panGesture)
  }

  @objc func didPan(panGesture: UIPanGestureRecognizer){
    switch panGesture.state {
    case .began:
      prevLocation = panGesture.location(in: view)
    case .changed, .ended:
      let nextLocation = panGesture.location(in: view)
      let angle = circle.angleBetween(firstPoint: prevLocation, secondPoint: nextLocation)

      rotatingViews.forEach({ $0.updatePosition(angle: angle)})
      prevLocation = nextLocation
    default: break
    }
  }
}


struct Circle {
  let center: CGPoint
  let radius: CGFloat

  func pointOnCircle(angle: CGFloat) -> CGPoint {
    let x = center.x + radius * cos(angle)
    let y = center.y + radius * sin(angle)

    return CGPoint(x: x, y: y)
  }

  func angleBetween(firstPoint: CGPoint, secondPoint: CGPoint) -> CGFloat {
    let firstAngle = atan2(firstPoint.y - center.y, firstPoint.x - center.x)
    let secondAnlge = atan2(secondPoint.y - center.y, secondPoint.x - center.x)
    let angleDiff = (firstAngle - secondAnlge) * -1

    return angleDiff
  }
}


class RotatingView: UIView {
  var currentAngle: CGFloat
  let circle: Circle

  init(circle: Circle, angle: CGFloat) {
    self.currentAngle = angle
    self.circle = circle
    super.init(frame: CGRect(x: 0, y: 0, width: 60, height: 60))
    center = circle.pointOnCircle(angle: currentAngle)
    backgroundColor = .blue
  }

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

  func updatePosition(angle: CGFloat) {
    currentAngle += angle
    center = circle.pointOnCircle(angle: currentAngle)
  }
}

Круг - это структура that просто держит центр всех видов, насколько далеко вы хотите их (радиус), и вспомогательные функции для вычисления углов, найденных в ссылке GitHub выше.

Вращающиеся виды - это виды, вращающиеся вокруг середины.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...