I have a requirement to move a label around only when the user touches on it and drags. I cant find out whether the user touched that label or not. I have used the touchesMoved method(Swift 2). Below is my code
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches as Set<UITouch>, withEvent: event)
let touch = touches.first
if (touch!.view) == (moveLabel as UIView) // moveButton is my label
{
location = touch!.locationInView(self.view)
moveLabel.center = location
}
}
When I do this, my label is not moving :(
someone help me please
I had been thinking in your question, and this is my result, you need to subclass UILabel and in the init you need to set userInteractionEnabled = true and then override this method override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) well, my code is this:
Swift 3.1 Code
import UIKit
class draggableLabel: UILabel {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.red.cgColor
self.isUserInteractionEnabled = true
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first;
let location = touch?.location(in: self.superview);
if(location != nil)
{
self.frame.origin = CGPoint(x: location!.x-self.frame.size.width/2, y: location!.y-self.frame.size.height/2);
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
Swift 2.2 Code
import UIKit
class draggableLabel: UILabel {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.redColor().CGColor
self.userInteractionEnabled = true
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first;
let location = touch?.locationInView(self.superview);
if(location != nil)
{
self.frame.origin = CGPointMake(location!.x-self.frame.size.width/2, location!.y-self.frame.size.height/2);
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
}
Hope this helps you, for me works great, this is how it works
Related
I have the following hierarchy, the root view of the controller. Added two subviews, a standard UIView and a UIScrollView. The UIView is on top.
What I want to do is to receive touches on that UIView, meaning touchesBegan, touchesMoved... like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Began")
super.touchesBegan(touches, with: event)
next?.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Moved")
super.touchesMoved(touches, with: event)
next?.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Ended")
super.touchesEnded(touches, with: event)
next?.touchesEnded(touches, with: event)
}
But at the same time I want the scroll to work. The UIScrollView that is below, I want that to keep scrolling as usual.
This seems impossible to do. If my view handles touches, then I can't forward them to the UIScrollView. But I want both things: see the raw touches on the top view while the scrollview to work as usual.
How can I accomplish this?
The opposite would also work. Meaning, a scrollview on top that scrolls as usual but I also receive the raw touches (touchesBegan, touchesMoved...) on the view that is underneath.
Following this answer worked for me, though with some slight changes. Do note that for this solution, the UIScrollView is on top of the UIView. Firstly, you need to create a subclass of UIScrollView and override the touch methods.
protocol CustomScrollViewDelegate {
func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?)
}
class CustomScrollView: UIScrollView {
var customDelegate: CustomScrollViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
for gesture in self.gestureRecognizers ?? [] {
gesture.cancelsTouchesInView = false
gesture.delaysTouchesBegan = false
gesture.delaysTouchesEnded = false
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchBegan(touches, with: event)
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchMoved(touches, with: event)
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchEnded(touches, with: event)
super.touchesEnded(touches, with: event)
}
}
Take note of the code in awakeFromNib(). UIScrollView has its own set of gestures. So for each gesture, delaysTouchesBegan and delaysTouchesEnded needs to be false to prevent delays for the touch events.
Finally, just assign the delegate to your ViewController and implement the methods like so.
class ViewController: UIViewController {
#IBOutlet weak var scrollView: CustomScrollView!
#IBOutlet weak var touchView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.customDelegate = self
}
}
extension ViewController: CustomScrollViewDelegate {
func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Began")
}
}
}
func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Moved")
}
}
}
func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Ended")
}
}
}
}
I would like to make a UIView highlighted after it's pressed and return to the normal color after release. What is the best practise for doing this?
Subclass the UIView:
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.blue
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
backgroundColor = UIColor.red
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
backgroundColor = UIColor.blue
}
}
Apple about overriding touchesBegan and touchesEnded:
When creating your own subclasses, call super to forward any events
that you do not handle yourself. If you override this method without
calling super (a common use pattern), you must also override the other
methods for handling touch events [i.e. touchesEnded, touchesMoved, touchesCancelled], even if your implementations do
nothing.
Further reading:
https://developer.apple.com/documentation/uikit/uiview
Proper practice for subclassing UIView?
Very simple example - you can run it in a Playground page:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func viewDidLoad() {
view.backgroundColor = .red
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.backgroundColor = .green
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
view.backgroundColor = .red
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
In practice, though, you want some additional code to check state, handle touchesCancelled, etc.
This is just to get you going - read up on touch events at: https://developer.apple.com/documentation/uikit/uiview
Im using Xcode 7, Swift, and SpriteKit and I'm attempting to allow the user to use two fingers at once in my app.
Basically I have two halves to my screen, and I want separate touch-recognition for each side, and simultaneously.
Here is my current code below :
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
guard let touch = touches.first else {
return;
}
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
if (touchedNode == touch1){
//code1
}
else if (touchedNode == touch2){
//code2
}
}
touch1 and touch2 are SkSpriteNodes that each take up a different half of the screen.
This code works well, as long as you only have 1 finger on the screen at a time.
However if there are two(1 for each half), which ever one was placed on the screen first is the one that is registered.
How do I make it so that both are being registered, and therefore code1 and code2 are being run?
You need multipleTouchEnabled property set to true. From the docs about this property :
When set to YES, the receiver receives all touches associated with a
multi-touch sequence. When set to NO, the receiver receives only the
first touch event in a multi-touch sequence. The default value of this
property is NO.
EDIT:
Based on your comments, you might try this (making sprites responsive to touches):
class Button:SKSpriteNode {
init(size:CGSize, color:SKColor) {
super.init(texture: nil, color: color, size: size)
userInteractionEnabled = true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let name = self.name {
print("Button with \(name) pressed")
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let name = self.name {
print("Button with \(name) pressed")
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let name = self.name {
print("Button with \(name) released")
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
let left = Button(size: CGSize(width: frame.size.width/2.0, height: frame.size.height), color: .blackColor())
left.name = "left"
left.position = CGPoint(x: left.size.width/2.0, y: frame.midY)
let right = Button(size: CGSize(width: frame.size.width/2.0, height: frame.size.height), color: .whiteColor())
right.name = "right"
right.position = CGPoint(x:frame.maxX-right.size.width/2.0, y: frame.midY)
addChild(left)
addChild(right)
}
}
Well, I says on the lines changeColorTo(touch: touches.anyObject() as? UITouch)
this error "value of type "Set" has no member "anyObject"
How can I fix the problem?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
changeColorTo(touch: touches.anyObject() as? UITouch)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
changeColorTo(touch: touches.anyObject() as? UITouch)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
changeColorTo(touch: touches.anyObject() as? UITouch)
}
func changeColorTo(touch touch: UITouch? ) {
if let contact = touch {
changeColorTo(point: contact.locationInView(self))
}
}
func changeColorTo(point point: CGPoint ) {
// Change the color model to the color displayed at the given view coordinate
if let color = colorModel {
let bounds = self.bounds
if bounds.contains(point) {
color.hue = Float((point.x-bounds.minX)/bounds.width*360)
color.saturation = Float((point.y-bounds.minY)/bounds.height*100)
}
}
}
Thanks in advance!
You should not pass UITouch set to CGPoint.
Try the below to solve the issues,
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject()! as UITouch
let location = touch.locationInView(self)
changeColorTo(location)
}
func changeColorTo(point: CGPoint ) {
}
Im making a subclass of UIButton to have a custom sublayer on the buttons layer.
I make this button a System type in storyboard and just replace class from UIButton my subclass in storyboard under Identity inspector.
I use System type button instead of custom to get that fade animation on titleLabel.
Now, I want to fade the sublayer aswell when the titleLabel fades.
Ive tried the begin/continue/endTrackingWithTouch: methods but it doesnt change when the titleLabel changes.
class TAButton:UIButton {
let innerShadowLayer = CFInnerShadow() // Custom class of CAGradientLayer
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool {
innerShadowLayer.opacity = 0.5
return super.beginTrackingWithTouch(touch, withEvent: event)
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool {
let con = super.continueTrackingWithTouch(touch, withEvent: event)
println(con) // I expected this to change to false when titleLabel fades but I was wrong, its always true
return con
}
override func endTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) {
innerShadowLayer.opacity = 1.0
super.endTrackingWithTouch(touch, withEvent: event)
}
func setup() {
self.setBackgroundGradient(UIColor(hex: 0x4A9BCB, alpha: 0.25), color2: UIColor(hex: 0xBAE5FF, alpha: 0.25)) // Custom function Im using
self.makeRoundSides() // Custom function Im using
innerShadowLayer.frame = self.bounds
innerShadowLayer.cornerRadius = self.layer.cornerRadius
innerShadowLayer.innerShadowRadius = 10
innerShadowLayer.innerShadowOpacity = 0.6
innerShadowLayer.innerShadowColor = UIColor(hex: 0x69FFF7, alpha: 1.0).CGColor
self.layer.addSublayer(innerShadowLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
}
This seems to work for me if I override touchesBegan and touchesEnded instead.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
innerShadowLayer.opacity = 0.5
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
innerShadowLayer.opacity = 1.0
}