Reputation: 2102
I have a container called ChartView
that holds my planet's UIImageView
. I adds my planets programatically then I assign them the UIPanGestureRecognizer
. From what I understand, I need to forward the delegate to my container ViewController then implements my draggedView()
function.
I successfully enter that function but the UIImageView doesn't move visually even though I'm printing its new position in draggedView(). What did I do wrong?
**** UPDATED ****
import UIKit
extension FloatingPoint {
var degreesToRadians: Self { return self * .pi / 180 }
var radiansToDegrees: Self { return self * 180 / .pi }
}
extension CGFloat {
static func random() -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UInt32.max)
}
}
extension UIColor {
static func random() -> UIColor {
return UIColor(red: .random(),
green: .random(),
blue: .random(),
alpha: 1)
}
}
class FirstViewController: UIViewController, UIGestureRecognizerDelegate {
@IBOutlet weak var chartView: ChartView!
private var planets: [Planet] = []
override func viewDidLoad() {
super.viewDidLoad()
self.chartView.delegate = self
self.chartView.setSegmentValues(
values: [50, 50, 50,50,50,50,50,50,50,50,50,50],
totals: [50, 50, 50,50,50,50,50,50,50,50,50,50])
self.chartView.addPlanet(fullAngle: CGFloat(180),name: "test")
}
@objc func didPan(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: self.view)
if let view = sender.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
sender.setTranslation(CGPoint.zero, in: self.view)
print("Pos: \(translation.x)- \(translation.y)")
}
}
Here's the ChartView:
import UIKit
import Foundation
@IBDesignable class ChartView: UIView {
var delegate: UIGestureRecognizerDelegate?
// Static value
private let ELEMENT_COLOR: [UIColor] = [UIColor.red, UIColor.gray, UIColor.blue, UIColor.yellow, UIColor.cyan]
//Values for each segment
private var segmentValues : [Float]
//Total for each segment
private var segmentTotals : [Float]
//Sum of each segmentTotals element
private var segmentTotalAll : Float
override init(frame: CGRect) {
segmentValues = []
segmentTotals = []
segmentTotalAll = 0
super.init(frame: frame)
self.isUserInteractionEnabled = true
self.backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
segmentValues = []
segmentTotals = []
segmentTotalAll = 0
super.init(coder: aDecoder)
self.isUserInteractionEnabled = true
self.backgroundColor = UIColor.clear
}
override func draw(_ rect: CGRect) {
// Base Circle
UIColor.random().setFill()
let outerPath = UIBezierPath(ovalIn: rect)
outerPath.fill()
// Semicircles
//self.frame isn't defined yet, so we can't use self.center
let viewCenter = CGPoint(x: rect.width / 2, y: rect.height / 2)
var i: Int = 0
var lastAngle :Float = 0.0
let baseCircleRadius = rect.width / 2 - 1.5
let centerCircleRadius = rect.width / 2 * 0.4
//value : current number
for value in segmentValues {
//total : total number
let total = segmentTotals[i]
//offsetTotal : difference between Base Circle and Center Circle
let offset = baseCircleRadius - centerCircleRadius
//radius : radius of segment
let radius = CGFloat(value / total) * offset + centerCircleRadius
//startAngle : start angle of this segment
let startAngle = lastAngle
//endAngle : end angle of this segment
let endAngle = lastAngle + total / segmentTotalAll * 360.0
//color : color of the segment
let color = self.ELEMENT_COLOR[i % 4]
color.setFill()
let midPath = UIBezierPath()
midPath.move(to: viewCenter)
midPath.addArc(withCenter: viewCenter, radius: CGFloat(radius), startAngle: CGFloat(startAngle.degreesToRadians), endAngle: CGFloat(endAngle.degreesToRadians), clockwise: true)
midPath.close()
UIColor.black.setStroke()
midPath.lineWidth = 2
midPath.stroke()
midPath.fill()
lastAngle = endAngle
i += 1
}
//Center circle
UIColor.white.setFill()
let centerPath = UIBezierPath(ovalIn: rect.insetBy(dx: rect.width / 2 * 0.4, dy: rect.height / 2 * 0.4))
UIColor.black.setStroke()
centerPath.lineWidth = 3
centerPath.stroke()
centerPath.fill()
}
//Sets all the segment members in order to draw each segment
func setSegmentValues(values : [Int], totals : [Int]){
//Must be equal lengths
if values.count != totals.count{
return;
}
//Set the colors
segmentTotalAll = 0
for total in totals {
segmentTotalAll += Float(total)
segmentTotals.append(Float(total))
}
for val in values {
segmentValues.append(Float(val))
}
}
public func addPlanet(fullAngle: CGFloat, name: String) {
let planetPosition: CGPoint = self.getPosition(center: CGPoint(x: self.frame.width/2, y: self.frame.height/2), radius: self.bounds.width/4, angle: fullAngle)
let planetImageView: UIImageView = UIImageView()
planetImageView.frame = CGRect(origin: planetPosition, size: CGSize(width: 10, height: 10))
planetImageView.center = planetPosition
planetImageView.backgroundColor = .blue
self.addSubview(planetImageView)
let panGesture = UIPanGestureRecognizer(target: self.delegate, action: #selector(FirstViewController.didPan(sender:)))
panGesture.delegate = self.delegate
planetImageView.isUserInteractionEnabled = true
planetImageView.addGestureRecognizer(panGesture)
// paint curve for sun
let path = UIBezierPath(arcCenter: CGPoint(x: self.frame.width/2, y: self.frame.height/2), radius: self.bounds.width/4, startAngle: CGFloat(0), endAngle: fullAngle.degreesToRadians, clockwise: true)
let animation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.position))
animation.duration = 1.5
animation.isRemovedOnCompletion = false // do not remove the animation effect, no state changes.
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
animation.fillMode = kCAFillModeBoth // keep to value after finishing
animation.path = path.cgPath
planetImageView.layer.add(animation, forKey: animation.keyPath)
}
private func getPosition(center: CGPoint, radius: CGFloat, angle: CGFloat) -> CGPoint {
return CGPoint(x: center.x + radius * cos(angle.degreesToRadians), y: center.y + radius * sin(angle.degreesToRadians))
}
public func addCenterCircle(){
let centerCircle = UIBezierPath(arcCenter: CGPoint(x: self.bounds.width/2,y: self.bounds.height/2), radius: self.bounds.width/4, startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = centerCircle.cgPath
//change the fill color
shapeLayer.fillColor = UIColor.white.cgColor
//you can change the stroke color
shapeLayer.strokeColor = UIColor.black.cgColor
//you can change the line width
shapeLayer.lineWidth = 1
self.layer.addSublayer(shapeLayer)
}
}
Upvotes: 0
Views: 69
Reputation: 535139
The problem with this question is that you're lying. Not on purpose, perhaps! But the simple fact is that your code, as shown, does work. Therefore the problem lies elsewhere. Here I am, dragging your "planet" in your "chart view":
The code I used is all your code. All I did was reduce it still further. This is the complete code of my example:
class FirstViewController: UIViewController {
@IBOutlet weak var chartView: ChartView!
override func viewDidLoad() {
super.viewDidLoad()
self.chartView.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.chartView.addPlanet(fullAngle: CGFloat(0),name: "name")
}
@objc func draggedView(_ sender:UIPanGestureRecognizer){
let translation = sender.translation(in: self.chartView)
if let view = sender.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
sender.setTranslation(CGPoint.zero, in: self.chartView)
}
}
extension FirstViewController: UIGestureRecognizerDelegate {
}
@IBDesignable class ChartView: UIView {
var delegate: FirstViewController?
public func addPlanet(fullAngle: CGFloat, name: String) {
let planetImageView: UIImageView = UIImageView()
planetImageView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 10, height: 10))
planetImageView.backgroundColor = .blue
let panGesture = UIPanGestureRecognizer(target: self.delegate, action: #selector(self.delegate?.draggedView(_:)))
planetImageView.isUserInteractionEnabled = true
planetImageView.addGestureRecognizer(panGesture)
self.addSubview(planetImageView)
}
}
Do you recognize that code? You should! It is your code, completely unchanged. I cut some of it, and I replaced the network stuff with this line, so we would have just one "planet":
self.chartView.addPlanet(fullAngle: CGFloat(0),name: "name")
And it works! Therefore I conclude that you are lying; the code you are showing is not the code that creates the non-draggable planet.
Upvotes: 2
Reputation: 879
I've created a simple blue square, then add a red planet. You can pan a red square. Here is the example, programmatically.
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
private lazy var chartView: ChartView = {
let cv = ChartView(frame: CGRect(x: 0,
y: 0,
width: view.frame.width - 20,
height: view.frame.height/2))
cv.center = view.center
// Important
cv.panGesture.delegate = self
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(chartView)
chartView.addPlanet(fullAngle: 50, name: "Mars")
}
}
class ChartView: UIView {
lazy var panGesture: UIPanGestureRecognizer = {
let v = UIPanGestureRecognizer(target: self, action: #selector(didPan(sender:)))
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func didPan(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: self)
if let view = sender.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
sender.setTranslation(CGPoint.zero, in: self)
print("Pos: \(translation.x)- \(translation.y)")
}
func addPlanet(fullAngle: CGFloat, name: String) {
let planetImageView: UIImageView = UIImageView()
planetImageView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 100, height: 100))
planetImageView.backgroundColor = .red
planetImageView.addGestureRecognizer(panGesture)
planetImageView.isUserInteractionEnabled = true
self.addSubview(planetImageView)
}
}
Upvotes: 2