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
}
Related
I have created Circle using "draw" and can place it anywhere on view. but when I want to move a specific circle by clicking it. it selects "LAST" created circle.
how can I select a specific circle? please guide me for the same.
if anything else you need let me know.
**CircleView**
import UIKit
class CircleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
}
required init(coder aDecoder : NSCoder) {
fatalError("init(coder : ) has not been implemented")
}
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext(){
context.setLineWidth(2)
UIColor.yellow.set()
let circleCenter = CGPoint(x: frame.size.width / 2, y: frame.self.height / 2)
let circleRadius = (frame.size.width - 10) / 2
context.addArc(center: circleCenter, radius: circleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
context.strokePath()
}
}
}
**ViewController**
import UIKit
class ViewController: UIViewController {
var circleView = CircleView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
let circleWidth = CGFloat(100)
var lastCircleCenter = CGPoint()
var currentCenter = CGPoint()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
circleView.isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
lastCircleCenter = touch.location(in: view)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let circleCenter = touch.location(in: view)
if circleCenter == lastCircleCenter{
let circleHeight = circleWidth
circleView = CircleView(frame: CGRect(x: circleCenter.x - circleWidth / 2, y: circleCenter.y - circleWidth / 2, width: 100, height: 100))
view.addSubview(circleView)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
let location = touch.location(in: view)
circleView.center = location
}
}
how can I select a specific circle? please guide me for the same.
if anything else you need let me know.
how can I select a specific circle? please guide me for the same.
if anything else you need let me know.
If you are creating multiple circles and adding them to your view, I would suggest to keep track of the created circles in a collection. That way on each touch you can check if the coordinate matches any of the created circles. Based on that you can determine if to create a new circle or to move an existing one.
Example:
class ViewController: UIViewController {
var circleViews: [CircleView] = []
let circleWidth = CGFloat(100)
var draggedCircle: CircleView?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Do nothing if a circle is being dragged
// or if we do not have a coordinate
guard draggedCircle == nil, let point = touches.first?.location(in: view) else {
return
}
// Do not create new circle if touch is in an existing circle
// Keep the reference of the (potentially) dragged circle
if let draggedCircle = circleViews.filter({ UIBezierPath(ovalIn: $0.frame).contains(point) }).first {
self.draggedCircle = draggedCircle
return
}
// Create new circle and store in an array
let offset = circleWidth / 2
let rect = CGRect(x: point.x - offset, y: point.y - offset, width: circleWidth, height: circleWidth)
let circleView = CircleView(frame: rect)
circleViews.append(circleView)
view.addSubview(circleView)
// The newly created view can be immediately dragged
draggedCircle = circleView
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// If touches end then a circle is never dragged
draggedCircle = nil
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let draggedCircle = draggedCircle, let point = touches.first?.location(in: view) else {
return
}
draggedCircle.center = point
}
}
Hello I'm trying to draw some rectangles on a UIImageView but when I add the background image the rectangle drawing is not working anymore:
This is the code:
import UIKit
class DrawView: UIView {
var startPos: CGPoint?
var endPos: CGPoint?
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {
return
}
if startPos != nil && endPos != nil {
print("both there")
drawRectangle(context: context, startPoint: startPos!, endPoint: endPos!, isFilled: false)
}
}
public func drawRectangle(context: CGContext, startPoint: CGPoint, endPoint: CGPoint, isFilled: Bool) {
let xx = startPoint
let xy = CGPoint(x:endPoint.x, y:startPoint.y)
let yx = CGPoint(x:startPoint.x, y:endPoint.y)
let yy = endPoint
print("draw rectangle")
context.move(to: xx)
context.addLine(to: xx)
context.addLine(to: xy)
context.addLine(to: yy)
context.addLine(to: yx)
context.addLine(to: xx)
context.setLineCap(.square)
context.setLineWidth(8)
if isFilled {
context.setFillColor(UIColor.purple.cgColor)
context.fillPath()
} else {
context.setStrokeColor(UIColor.red.cgColor)
context.strokePath()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self)
print(position)
startPos = position
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self)
print(position)
endPos = position
setNeedsDisplay()
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self)
print(position)
endPos = position
setNeedsDisplay()
}
}
func addBackground() {
// screen width and height:
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
imageView.image = UIImage(named: "img1.jpg")
// you can change the content mode:
imageView.contentMode = UIView.ContentMode.scaleAspectFill
self.addSubview(imageView)
self.sendSubviewToBack(imageView)
}
}
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var drawView: DrawView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
drawView.addBackground()
}
}
Im suspecting that I'm not using the correct UIGraphicsGetCurrentContext after setting the image but I was not able to figure it out how to fix it.
when I start to draw on large UIView ( width: 3700 , height: 40000 ), it takes a lot of memory
when app starts, memory is 150 MB and when start drawing on it( calling setNeedsDisplay method) take around 1 GB and app is gonna crash
class DrawingVc: UIViewController {
let contentView = DrawableView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.backgroundColor = .clear
self.view.addSubview(contentView)
contentView.frame = CGRect(x: 0, y: 0, width:view.frame.width, height:
view.frame.height * 50)
}
here is the code of custom view, as you can see, setNeedsDisplay runs on touchMoves
class DrawableView: UIView {
var mLastPath: UIBezierPath?
weak var scdelegate: DrawableViewDelegate?
var isDrawEnable = true
private var drawingLines : [UIBezierPath] = []
override init(frame: CGRect) {
super.init(frame: frame)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
debugPrint("request draw")
drawLine()
}
private func drawLine() {
UIColor.blue.setStroke()
for line in drawingList {
line.lineWidth = 4
line.stroke()
line.lineCapStyle = .round
}
}
var drawingList = [UIBezierPath]()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if touches.count == 2 {
return
}
let location = (touches.first?.location(in: self))!
mLastPath = UIBezierPath()
mLastPath?.move(to: location)
prevPoint = location
drawingList.append(mLastPath!)
}
var prevPoint: CGPoint?
var isFirst = true
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
debugPrint("touchesMoved: " , (touches.first?.location(in: self).x)! , (touches.first?.location(in: self).y)! )
if let coalescedtouches = event?.coalescedTouches(for: touches.first!)
{
for coalescedTouch in coalescedtouches
{
let locationInView = coalescedTouch.location(in: self)
if let prevPoint = prevPoint {
let midPoint = CGPoint( x: (locationInView.x + prevPoint.x) / 2, y: (locationInView.y + prevPoint.y) / 2)
if isFirst {
mLastPath?.addLine(to: midPoint)
}else {
mLastPath?.addQuadCurve(to: midPoint, controlPoint: prevPoint)
}
isFirst = false
} else {
mLastPath?.move(to: locationInView)
}
prevPoint = locationInView
}
}
setNeedsDisplay()
}
}
What makes this problem and how that fix?
Your view is larger than the largest possible screen on an iOS device, so I suppose your view is embedded in a scrollview. You should only draw the visible parts of your view. Unfortunately, this is not supported by UIView directly. You may take a look on CATiledLayer, which supports drawing of only visible parts of a layer, and it supports different levels of details for zoomed layers, too.
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 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)
}
}