将一个圆形的UIView移动到另一个圆形的UIView内部。

huangapple go评论82阅读模式
英文:

Move a circular UIView inside another circular UIView

问题

I'm trying to do a joystick swift, and I'm almost there.

但我有一个问题,当我将操纵杆移动到"中间"时,操纵杆的移动很流畅,但当操纵杆触及"其容器"的边缘时,它变得卡顿。

但我知道为什么,这是因为只有在操纵杆不触及边缘时,我才允许操纵杆移动,我不知道如何解决这个问题(在else中放置什么代码)。

这是我的代码和一个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 {
            // 我不知道在这里放什么代码可以使操纵杆更"流畅"
        }

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

将一个圆形的UIView移动到另一个圆形的UIView内部。

感谢您的帮助。

英文:

I'm trying to do a joystick swift, and I'm almost there.

But I have a problem, the movement of the joystick is smooth when I move it "in the middle", but when the joystick touches the edges of "its container" it becomes laggy.

But I know why, it's because I allow the joystick to move only if it doesn't touch the edges, and I don't know how to correct this problem (what code to put in the else).

Here's my code and a GIF so you can see better.

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)
    }
}

将一个圆形的UIView移动到另一个圆形的UIView内部。

Thank you for your help

答案1

得分: 1

以下是已翻译的部分:

这是一种方法...

  • 计算外圆心到内圆心的最大可用距离,作为半径
  • 跟踪触摸/滑动手势相对于外圆心的位置
  • 如果内圆心(触摸点)到外圆心的新距离大于最大半径,请将内圆心移动到触摸到中心线与半径圆边缘的交点

这是它的外观,"摇杆" 视图的中心用绿点标识,半径圆以红色轮廓显示:

将一个圆形的UIView移动到另一个圆形的UIView内部。

您可以尝试使用此代码:

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()
        }
        
        // 如果要使 "摇杆" 圆稍微重叠 "外圆" 一点,请调整此值
        innerRadius = (substractSize - joystickSize) * 0.5
        
        // 开始调试/澄清...
        // 添加一个 "点" 到摇杆视图的中心
        // 添加一个显示内半径的红色圆圈 - 我们希望限制摇杆视图的中心的地方
        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()
        }

        // 结束调试/澄清
        
    }
    
    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 distance > innerRadius {
            newCenter = pointOnLine(from: outerCircleViewCenter, to: touchLocation, distance: innerRadius)
        }
        
        joystickView.center = newCenter

    }
}

注意: 您可以删除(或注释掉)viewDidLoad() 中位于 // 开始调试// 结束调试 注释之间的代码行,以删除绿色中心点和红色圆圈。

英文:

Here is one approach...

  • calculate the maximum available distance from the center of the outer circle to the center of the inner circle, as a radius
  • track the touch / pan gesture's location relative to the center of the outer circle
  • if the new distance from the center of the inner circle (the touch point) to the center of the outer circle is greater than the max radius, move the inner circle center to the intersection of the touch-to-center line and the edge of the radius circle

Here's how it would look, with the center of the "joystick" view identified with a green dot, and the radius circle shown as a red outline:

将一个圆形的UIView移动到另一个圆形的UIView内部。

You can give it a try with this code:

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
}
}

Note: you can delete (or comment-out) the lines of code in viewDidLoad() between the // start debugging and // end debugging comments to remove the green center-dot and the red circle.

huangapple
  • 本文由 发表于 2020年1月7日 01:58:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/59616783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定