Переместить круговой UIView в другой круговой UIView - PullRequest
0 голосов
/ 06 января 2020

Я пытаюсь сделать джойстик быстрым, и я почти на месте.

Но у меня проблема, движение джойстика плавное, когда я перемещаю его "в середине", но когда джойстик касается краев «его контейнера», он становится запаздывающим.

Но я знаю почему, потому что я позволяю джойстику двигаться только, если он не касается краев, и я не знаю как исправить эту проблему (какой код добавить в другом).

Вот мой код и GIF, чтобы вы могли лучше видеть.

import UIKit
import SnapKit

class ViewController: UIViewController {

    let joystickSize = 150
    let substractSize = 200
    let joystickOffset = 10

    let joystickSubstractView = UIView()
    let joystickView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()

        joystickSubstractView.backgroundColor = .gray
        joystickSubstractView.layer.cornerRadius = CGFloat(substractSize / 2)
        self.view.addSubview(joystickSubstractView)

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragJoystick))
        joystickView.isUserInteractionEnabled = true
        joystickView.addGestureRecognizer(panGesture)
        joystickView.backgroundColor = .white
        joystickView.layer.cornerRadius = CGFloat(joystickSize / 2)
        joystickSubstractView.addSubview(joystickView)

        joystickSubstractView.snp.makeConstraints {
            $0.width.height.equalTo(substractSize)
            $0.centerX.equalToSuperview()
            $0.bottom.equalToSuperview().inset(150)
        }

        joystickView.snp.makeConstraints {
            $0.width.height.equalTo(joystickSize)
            $0.center.equalToSuperview()
        }
    }

    @objc func dragJoystick(_ sender: UIPanGestureRecognizer) {
        self.view.bringSubviewToFront(joystickView)
        let translation = sender.translation(in: self.view)

        let joystickCenter = joystickView.convert(joystickView.center, to: self.view)
        let futureJoystickCenter =  CGPoint(x: joystickCenter.x - joystickView.frame.minX + translation.x,
                                            y: joystickCenter.y - joystickView.frame.minY + translation.y)
        let distanceBetweenCenters = hypot(futureJoystickCenter.x - joystickSubstractView.center.x,
                                           futureJoystickCenter.y - joystickSubstractView.center.y)

        if CGFloat(substractSize / 2 + joystickOffset) >= (distanceBetweenCenters + CGFloat(joystickSize / 2)) {
            joystickView.center = CGPoint(x: joystickView.center.x + translation.x,
                                          y: joystickView.center.y + translation.y)
        } else {
            // I don't know what to put here to make the joystick "smoother"
        }

        sender.setTranslation(CGPoint.zero, in: self.view)
    }
}

GIF

Спасибо за помощь

1 Ответ

1 голос
/ 06 января 2020

Вот один подход ...

  • рассчитать максимально доступное расстояние от центра внешнего круга до центра внутреннего круга, так как радиус
  • отслеживает положение жеста касания / панорамирования относительно центра внешнего круга
  • , если новое расстояние от центра внутреннего круга (точки касания) до центра внешнего круга больше максимального радиуса, переместите центр внутреннего круга до пересечения линии соприкосновения с центром и края круга радиуса

Вот как это будет выглядеть, при этом центр вида «джойстик» обозначен зеленым точка и радиусный круг, показанный красным контуром:

enter image description here

Вы можете попробовать его с этим кодом:

class JoyStickViewController: UIViewController {

    let joystickSize: CGFloat = 150
    let substractSize: CGFloat = 200

    var innerRadius: CGFloat = 0.0

    let joystickSubstractView = UIView()
    let joystickView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()

        joystickSubstractView.backgroundColor = .gray
        joystickSubstractView.layer.cornerRadius = CGFloat(substractSize / 2)
        self.view.addSubview(joystickSubstractView)

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragJoystick(_:)))
        joystickView.isUserInteractionEnabled = true
        joystickView.addGestureRecognizer(panGesture)
        joystickView.backgroundColor = .yellow
        joystickView.layer.cornerRadius = CGFloat(joystickSize / 2)
        joystickSubstractView.addSubview(joystickView)

        joystickSubstractView.snp.makeConstraints {
            $0.width.height.equalTo(substractSize)
            $0.centerX.equalToSuperview()
            $0.bottom.equalToSuperview().inset(150)
        }

        joystickView.snp.makeConstraints {
            $0.width.height.equalTo(joystickSize)
            $0.center.equalToSuperview()
        }

        // if you want the "joystick" circle to overlap the "outer circle" a bit, adjust this value
        innerRadius = (substractSize - joystickSize) * 0.5


        // start debugging / clarification...
        // add a center "dot" to the joystick view
        // add a red circle showing the inner radius - where we want to restrict the center of the joystick view
        let jsCenterView = UIView()
        jsCenterView.backgroundColor = .green
        jsCenterView.layer.cornerRadius = 2.0
        joystickView.addSubview(jsCenterView)
        jsCenterView.snp.makeConstraints {
            $0.width.height.equalTo(4.0)
            $0.center.equalToSuperview()
        }

        let v = UIView()
        v.backgroundColor = .clear
        v.layer.borderColor = UIColor.red.cgColor
        v.layer.borderWidth = 2
        v.layer.cornerRadius = innerRadius
        v.isUserInteractionEnabled = false
        joystickSubstractView.addSubview(v)
        v.snp.makeConstraints {
            $0.width.height.equalTo(innerRadius * 2.0)
            $0.center.equalToSuperview()
        }

        // end debugging / clarification

    }

    func lineLength(from pt1: CGPoint, to pt2: CGPoint) -> CGFloat {
        return hypot(pt2.x - pt1.x, pt2.y - pt1.y)
    }

    func pointOnLine(from startPt: CGPoint, to endPt: CGPoint, distance: CGFloat) -> CGPoint {
        let totalDistance = lineLength(from: startPt, to: endPt)
        let totalDelta = CGPoint(x: endPt.x - startPt.x, y: endPt.y - startPt.y)
        let pct = distance / totalDistance;
        let delta = CGPoint(x: totalDelta.x * pct, y: totalDelta.y * pct)
        return CGPoint(x: startPt.x + delta.x, y: startPt.y + delta.y)
    }

    @objc func dragJoystick(_ sender: UIPanGestureRecognizer) {

        let touchLocation = sender.location(in: joystickSubstractView)

        let outerCircleViewCenter = CGPoint(x: joystickSubstractView.bounds.width * 0.5, y: joystickSubstractView.bounds.height * 0.5)

        var newCenter = touchLocation

        let distance = lineLength(from: touchLocation, to: outerCircleViewCenter)

        // if the touch would put the "joystick circle" outside the "outer circle"
        // find the point on the line from center to touch, at innerRadius distance
        if distance > innerRadius {
            newCenter = pointOnLine(from: outerCircleViewCenter, to: touchLocation, distance: innerRadius)
        }

        joystickView.center = newCenter

    }

}

Примечание: вы можете удалить (или закомментировать) строки кода в viewDidLoad() между комментариями // start debugging и // end debugging, чтобы удалить зеленую центральную точку и красный круг.

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