在另一个圆形 UIView 中移动一个圆形 UIView
Move a circular UIView inside another circular UIView
我正在尝试做一个操纵杆swift,我快完成了。
但是我有一个问题,当我移动摇杆时,摇杆的运动是平滑的"in the middle",但是当摇杆接触到"its container"的边缘时,它变得迟钝。
但我知道为什么,因为我只允许摇杆在不碰到边缘的情况下移动,我不知道如何纠正这个问题(在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 {
[=10=].width.height.equalTo(substractSize)
[=10=].centerX.equalToSuperview()
[=10=].bottom.equalToSuperview().inset(150)
}
joystickView.snp.makeConstraints {
[=10=].width.height.equalTo(joystickSize)
[=10=].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)
}
}
感谢您的帮助
这是一种方法...
- 计算外圆圆心到内圆圆心的最大可用距离,作为半径
- 跟踪触摸/平移手势相对于外圆中心的位置
- 如果新的内圆圆心(触摸点)到外圆圆心的距离大于最大半径,将内圆圆心移动到touch-to-center的交点直线和半径圆的边缘
这是它的外观,"joystick" 视图的中心用绿点标识,半径圆显示为红色轮廓:
您可以尝试使用此代码:
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 {
[=10=].width.height.equalTo(substractSize)
[=10=].centerX.equalToSuperview()
[=10=].bottom.equalToSuperview().inset(150)
}
joystickView.snp.makeConstraints {
[=10=].width.height.equalTo(joystickSize)
[=10=].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 {
[=10=].width.height.equalTo(4.0)
[=10=].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 {
[=10=].width.height.equalTo(innerRadius * 2.0)
[=10=].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
注释之间的代码行删除绿色中心点和红色圆圈。
我正在尝试做一个操纵杆swift,我快完成了。
但是我有一个问题,当我移动摇杆时,摇杆的运动是平滑的"in the middle",但是当摇杆接触到"its container"的边缘时,它变得迟钝。
但我知道为什么,因为我只允许摇杆在不碰到边缘的情况下移动,我不知道如何纠正这个问题(在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 {
[=10=].width.height.equalTo(substractSize)
[=10=].centerX.equalToSuperview()
[=10=].bottom.equalToSuperview().inset(150)
}
joystickView.snp.makeConstraints {
[=10=].width.height.equalTo(joystickSize)
[=10=].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)
}
}
感谢您的帮助
这是一种方法...
- 计算外圆圆心到内圆圆心的最大可用距离,作为半径
- 跟踪触摸/平移手势相对于外圆中心的位置
- 如果新的内圆圆心(触摸点)到外圆圆心的距离大于最大半径,将内圆圆心移动到touch-to-center的交点直线和半径圆的边缘
这是它的外观,"joystick" 视图的中心用绿点标识,半径圆显示为红色轮廓:
您可以尝试使用此代码:
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 {
[=10=].width.height.equalTo(substractSize)
[=10=].centerX.equalToSuperview()
[=10=].bottom.equalToSuperview().inset(150)
}
joystickView.snp.makeConstraints {
[=10=].width.height.equalTo(joystickSize)
[=10=].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 {
[=10=].width.height.equalTo(4.0)
[=10=].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 {
[=10=].width.height.equalTo(innerRadius * 2.0)
[=10=].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
注释之间的代码行删除绿色中心点和红色圆圈。