I am trying to rotate an UIImageView using Touch events, I want the image to rotate with my finger, I want to do the same as this: dropbox link (best to see it, so u understand what I mean)
After researching on how to rotate an UIImageView: How can I rotate an UIImageView by 20 degrees. I have tried the following code with my simple approach to developing iPhone apps:
class ViewController: UIViewController {
var startpoint:CGPoint!
var endpoint:CGPoint!
#IBOutlet var yello:UIImageView
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
let t:UITouch = touches.anyObject() as UITouch
startpoint = t.locationInView(self.view)
endpoint = t.previousLocationInView(self.view)
if t.view == yello {
var startX = startpoint.x
var endX = endpoint.x
yello.center = CGPointMake(yello.center.x, yello.center.y)
UIView.animateWithDuration(1, animations: { self.yello.transform = CGAffineTransformMakeRotation(startX-endX)})
}
}
}
I can see clearly that my code has a lot of mistakes and bad practices, and it also doesn't behave correctly as I want. (see dropbox link above)
So maybe there is a better way to do this perhaps by using CoreAnimation, and I would appreciate if code sample would do the same as I want.
I just wrote this real quick. I think it is what you are looking for. Instead of using images I used colored-views but you could easily replace that with an image. You may want to detect if the view was dragged so that the user must drag the view in-order to rotate, because currently the user can drag anywhere to rotate the view. (The code below now checks for this case) Let me know if you have any questions about it.
class ViewController: UIViewController {
var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
myView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
myView.center = self.view.center
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
myView.layer.anchorPoint = CGPoint(x: 1.0, y: 0.5)
let box = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
box.backgroundColor = UIColor.blueColor()
myView.addSubview(box)
}
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
let touch = touches.anyObject() as UITouch
if touch.view === myView.subviews[0] {
let position = touch.locationInView(self.view)
let target = myView.center
let angle = atan2(target.y-position.y, target.x-position.x)
myView.transform = CGAffineTransformMakeRotation(angle)
}
}
}
//updated code for swift 4
class ViewController: UIViewController {
var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
myView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
myView.center = self.view.center
myView.isUserInteractionEnabled = true
myView.backgroundColor = UIColor.red
self.view.addSubview(myView)
myView.layer.anchorPoint = CGPoint(x: 1.0, y: 0.5)
let box = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
box.backgroundColor = UIColor.blue
myView.addSubview(box)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch:UITouch = touches.first!
if touch.view === myView.subviews[0] {
let position = touch.location(in: self.view)
let target = myView.center
let angle = atan2(target.y-position.y, target.x-position.x)
myView.transform = CGAffineTransform(rotationAngle: angle)
}
}
Related
Im currently using IOS swift and I'm having trouble rotating the image view that I have on my screen to the point at which I touch the screen, So if tap directly underneath the image, it rotates 180 degrees to point at the touch point.
Here is the Code I have so far, I want to rotate the playerCharacter to wherever I touch the screen
import UIKit
import SpriteKit
class ViewController: UIViewController {
#IBOutlet weak var playerCharacter: UIImageView!
var enemyspeed: Timer?
let imageName = "yourImage.png"
let image = UIImage(named: "")
let imageView = UIImageView(image: nil)
override func viewDidLoad() {
super.viewDidLoad()
imageView.frame = CGRect(x: 1000, y: 700, width: 100, height: 100)
view.addSubview(imageView)
imageView.backgroundColor = .gray
}
#IBAction func beginGame(_ sender: UIButton) {
enemyspeed = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(astroid), userInfo: nil, repeats: true)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: view)
print(position)
UIView.animate(withDuration: 1, animations: {
self.playerCharacter.frame.origin.x = position.x
self.playerCharacter.frame.origin.y = position.y
})
}
}
#objc func astroid(){
let enemyPositionX = playerCharacter.frame.origin.x
let enemyPositionY = playerCharacter.frame.origin.y
UIView.animate(withDuration: 5, animations:{
self.imageView.frame.origin.x = enemyPositionX
self.imageView.frame.origin.y = enemyPositionY
})
}
}
You can use a little math to calculate the angle between the center of the imageView and the touch point:
let pt1 = imgView.center
let pt2 = touch.location(in: view)
let angle = atan2(pt2.y - pt1.y, pt2.x - pt1.x)
then rotate the image view:
imgView.transform = CGAffineTransform(rotationAngle: angle)
Here's a full example that uses a SF Symbol "arrow.right" image (so it starts pointing at Zero degrees), then the arrow tracks the touch location:
class AngleViewController: UIViewController {
let imgView: UIImageView = {
let v = UIImageView()
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// make sure we have an image
guard let img = UIImage(systemName: "arrow.right") else {
fatalError("Could not load image!!!!")
}
// set the image
imgView.image = img
// add the image view to the view
view.addSubview(imgView)
// 100x100 image view frame
imgView.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// let's put the image view in the center
imgView.center = view.center
}
func updateArrow(_ pt2: CGPoint) -> Void {
let pt1 = imgView.center
let angle = atan2(pt2.y - pt1.y, pt2.x - pt1.x)
imgView.transform = CGAffineTransform(rotationAngle: angle)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
updateArrow(touch.location(in: view))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
updateArrow(touch.location(in: view))
}
}
I would like to get the rectangular position in the scene view of a tapped node so that I can use that position to animate another UIView from that position to a bigger size (exactly like in the Measure app):
I get the tapped node with this code, though how do I get the rect or the dimensions or whatever to make the smooth transition from the node to the view?
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
guard let touch = touches.first else { return }
if (touch.view == self.sceneView) {
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
}
}
I've looked into using hitTest or smartHitTest on the scene view given the touch position but I can't really seem to be able to get the size/position of the smaller view.
Not exactly your answer but try it out: (edited to send it back)
var front = false
var boxView = UIView()
#objc func tap(_ sender: UITapGestureRecognizer){
if !front{
let position = sender.location(in: sceneView)
let hitTest = sceneView.hitTest(position, options: nil)
if let hitNode = hitTest.first?.node{
self.boxView = UIView(frame: CGRect(x: position.x, y: position.y, width: 100, height: 40))
self.boxView.backgroundColor = .red
sceneView.addSubview(self.boxView)
UIView.animate(withDuration: 0.2) {
self.boxView.frame = CGRect(x: self.sceneView.frame.width/2 - 150, y: self.sceneView.frame.height/2 - 100, width: 300, height: 200)
}
front = true
}
}else{
let p = plane.convertPosition(plane.position, to: sceneView.scene.rootNode)
print(p, plane.position) // see difference between p and plane.position
let projectedPoint = sceneView.projectPoint(p)
let point = CGPoint(x: CGFloat(projectedPoint.x), y: CGFloat(projectedPoint.y))
UIView.animate(withDuration: 0.2, animations: {
self.boxView.frame = CGRect(x: point.x, y: point.y, width: 100, height: 40)
}) { (completed) in
self.boxView.removeFromSuperview()
}
front = false
}
}
plane here is node in my test case. You might want to look about convertPosition and projectPoint
I managed to get a scrollview working and scrolling, but now when I go to scroll it only scrolls from right to left and was wondering how I go about reversing it so it scrolls from left to right instead.
Here is my menu code that contains my scrollview:
var moveableNode = SKNode()
var scrollView: CustomScrollView!
private var spriteSize = CGSize.zero
let kMargin: CGFloat = 40
var sprite = SKSpriteNode()
class Menu: SKScene {
override func didMoveToView(view: SKView) {
addChild(moveableNode)
spriteSize = SKSpriteNode (imageNamed: "card_level01").size
let initialMargin = size.width/2
let marginPerImage = kMargin + spriteSize.width
scrollView = CustomScrollView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height), scene: self, moveableNode: moveableNode)
scrollView.contentSize = CGSizeMake(initialMargin*2 + (marginPerImage * 7), size.height)
// scrollView.contentSize = CGSizeMake(self.frame.size.width * 2, self.frame.size.height)
view.addSubview(scrollView)
for i in 1...8 {
let sprite = SKSpriteNode(imageNamed: String(format: "card_level%02d", i))
sprite.position = CGPoint (x: initialMargin + (marginPerImage * (CGFloat(i) - 1)), y: size.height / 2)
moveableNode.addChild(sprite)
}
Here is my scrollView Class that is a subclass of UIScrollView:
var nodesTouched: [AnyObject] = [] // global
class CustomScrollView: UIScrollView {
// MARK: - Static Properties
/// Touches allowed
static var disabledTouches = false
/// Scroll view
private static var scrollView: UIScrollView!
private static var contentView: UIView!
// MARK: - Properties
/// Current scene
private var currentScene: SKScene?
/// Moveable node
private var moveableNode: SKNode?
// MARK: - Init
init(frame: CGRect, scene: SKScene, moveableNode: SKNode) {
print("Scroll View init")
super.init(frame: frame)
CustomScrollView.scrollView = self
currentScene = scene
self.moveableNode = moveableNode
self.frame = frame
indicatorStyle = .White
scrollEnabled = true
//self.minimumZoomScale = 1
//self.maximumZoomScale = 3
canCancelContentTouches = false
userInteractionEnabled = true
delegate = self
//flip for spritekit (only needed for horizontal)
let verticalFlip = CGAffineTransformMakeScale(-1,-1)
self.transform = verticalFlip
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Touches
extension CustomScrollView {
/// began
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Touch began scroll view")
guard !CustomScrollView.disabledTouches else { return }
currentScene?.touchesBegan(touches, withEvent: event)
}
/// moved
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Touch moved scroll view")
guard !CustomScrollView.disabledTouches else { return }
currentScene?.touchesMoved(touches, withEvent: event)
}
/// ended
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Touch ended scroll view")
guard !CustomScrollView.disabledTouches else { return }
currentScene?.touchesEnded(touches, withEvent: event)
}
/// cancelled
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
print("Touch cancelled scroll view")
guard !CustomScrollView.disabledTouches else { return }
currentScene?.touchesCancelled(touches, withEvent: event)
}
}
// MARK: - Touch Controls
extension CustomScrollView {
/// Disable
class func disable() {
print("Disabled scroll view")
CustomScrollView.scrollView?.userInteractionEnabled = false
CustomScrollView.disabledTouches = true
}
/// Enable
class func enable() {
print("Enabled scroll view")
CustomScrollView.scrollView?.userInteractionEnabled = true
CustomScrollView.disabledTouches = false
}
}
// MARK: - Delegates
extension CustomScrollView: UIScrollViewDelegate {
/// did scroll
func scrollViewDidScroll(scrollView: UIScrollView) {
print("Scroll view did scroll")
moveableNode!.position.x = scrollView.contentOffset.x // Left/Right
//moveableNode!.position.y = scrollView.contentOffset.y // Up/Dowm
}
}
You need get my updated helper on gitHub (v1.1) first before reading the rest.
My helper only really works well when your scene scale mode (gameViewController) is set to
.ResizeFill
so your scenes do not crop. If you use a different scaleMode such as
.AspectFill
than it might crop stuff in your scrollView which you would need to adjust for.
It also doesnt work if your game/app supports both portrait and landscape, which is unlikely for a game anyway.
So as I said and you have also noticed when using a ScrollView in spriteKit the coordinates are different compared to UIKit. For vertical scrolling this doesn't really mean anything, but for horizontal scrolling everything it is in reverse. So to fix this you do the following
Set up your scrollView for horizontal scrolling, passing along the new scrollDirection property (.Horizontal in this case)
scrollView = CustomScrollView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height), scene: self, moveableNode: moveableNode, scrollDirection: .Horizontal)
scrollView.contentSize = CGSizeMake(self.frame.size.width * 3, self.frame.size.height) // * 3 makes it twice as wide as screen
view.addSubview(scrollView)
you need to also add this line of code after adding it to the view.
scrollView.setContentOffset(CGPoint(x: 0 + self.frame.size.width * 2, y: 0), animated: true)
this is the line you use to tell the ScrollView on which page to start. Now in this example the scrollView is three times as wide as the screen, therefore you need to offset the content by 2 screen lengths
Now to make things easier for positioning I would do this, create sprites for each page of the scrollView. This makes positioning much easier later on.
let page1ScrollView = SKSpriteNode(color: SKColor.clearColor(), size: CGSizeMake(self.frame.size.width, self.frame.size.height))
page1ScrollView.position = CGPointMake(CGRectGetMidX(self.frame) - (self.frame.size.width * 2), CGRectGetMidY(self.frame))
moveableNode.addChild(page1ScrollView)
let page2ScrollView = SKSpriteNode(color: SKColor.clearColor(), size: CGSizeMake(self.frame.size.width, self.frame.size.height))
page2ScrollView.position = CGPointMake(CGRectGetMidX(self.frame) - (self.frame.size.width), CGRectGetMidY(self.frame))
moveableNode.addChild(page2ScrollView)
let page3ScrollView = SKSpriteNode(color: SKColor.clearColor(), size: CGSizeMake(self.frame.size.width, self.frame.size.height))
page3ScrollView.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
moveableNode.addChild(page3ScrollView)
and now you can positioning your actual labels, sprites much easier.
/// Test label page 1
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = "Hello, World!"
myLabel.fontSize = 45
myLabel.position = CGPointMake(0, 0)
page1ScrollView.addChild(myLabel)
/// Test sprite page 2
let sprite = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 50, height: 50))
sprite.position = CGPointMake(0, 0)
page2ScrollView.addChild(sprite)
/// Test sprite page 3
let sprite2 = SKSpriteNode(color: SKColor.blueColor(), size: CGSize(width: 50, height: 50))
sprite2.position = CGPointMake(0, 0)
page3ScrollView.addChild(sprite2)
Hope this helps.
I also updated my GitHub project to explain this better
https://github.com/crashoverride777/Swift2-SpriteKit-UIScrollView-Helper
I am trying to make a square appear on a random CGPoint in Swift every time I click the screen. Below is what I have done with no compiler errors, but once I run the application, I can click or drag to move my "person," but every time I do that, a smaller square does not appear. Any advice or solutions are great. Thank you! by the way, person, a UIImageView is no image but has a viable backgroundColor.`
`
#IBOutlet weak var person: UIImageView!
var location = CGPoint(x: 0,y: 0)
var randomPoint = CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
location = touch!.locationInView(self.view)
person.center = location
for _ in touches {
let mySquare: UIView = UIView(frame: CGRect(x: 0,y: 0,width: 20,height: 20))
mySquare.backgroundColor = UIColor.cyanColor()
mySquare.center = randomPoint
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
location = touch!.locationInView(self.view)
person.center = location
for _ in touches {
let mySquare: UIView = UIView(frame: CGRect(x: 0,y: 0,width: 20,height: 20))
mySquare.backgroundColor = UIColor.cyanColor()
mySquare.center = randomPoint
}
}`
For every touch, you just need create and add(you missed this step) square in touch begin (or touch end), don't do in touch move.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
location = touch!.locationInView(self.view)
person.center = location
for _ in touches {
let mySquare: UIView = UIView(frame: CGRect(x: 0,y: 0,width: 20,height: 20))
mySquare.backgroundColor = UIColor.cyanColor()
mySquare.center = randomPoint
view.addSubview(mySquare) // add square to the view
}
}
You didn't add mySquare view onto any view.
self.view.addSubview(mySquare)
I created a subclass of UIimageView because i want to use this imageview multiple times. I'm using touchesmoved to drag the image. So when i use multiple images of same class, i want to find whether they intersect each other. Here's the image class code
import UIKit
class imgBall: UIImageView {
private var xOffset: CGFloat = 0.0
private var yOffset: CGFloat = 0.0
required init(coder aDecoder:NSCoder) {
fatalError("use init(point:")
}
init(point:CGPoint) {
let image = UIImage(named: "ball.png")!
super.init(image:image)
self.frame = CGRect(origin: point, size: CGSize(width: 100, height: 100))
self.userInteractionEnabled = true
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let point = touches.anyObject()!.locationInView(self.superview)
xOffset = point.x - self.center.x
yOffset = point.y - self.center.y
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
let point = touches.anyObject()!.locationInView(self.superview)
self.center.x = point.x - xOffset
}
}
and this is how I'm using it in gameviewcontroller
viewDidLoad() {
var ball1 = imgBall(point: CGPoint(x: 20, y: 0))
self.view.addSubview(ball1)
var ball2 = imgBall(point: CGPoint(x: 50, y: 70))
self.view.addSubview(ball2)
}
so how do i find out if they intersect/collide each other?
You've got to use CGRectIntersectsRect with something like this :
if (CGRectIntersectsRect(ball1.frame,ball2.frame) {
// they intersect
} else {
// they're safe
}