IOS scrollview is not clickable - ios

Here is my code:
int i = 0;
self.userInteractionEnabled = YES;
self.exclusiveTouch = YES;
self.canCancelContentTouches = YES;
self.delaysContentTouches = YES;
self.translatesAutoresizingMaskIntoConstraints = YES;
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(gototest)];
tapGestureRecognizer.numberOfTapsRequired = 1;
[self addGestureRecognizer:tapGestureRecognizer];
for (NSString *message in self.messages) {
UILabel * label = [[UILabel alloc] initWithFrame:CGRectZero];
self.userInteractionEnabled = YES;
label.text = message;
label.tag = i;
CGSize size = [message sizeWithFont:label.font];
CGFloat width = size.width + kPADDING;
label.frame = CGRectMake(xPos, 0.0, width, self.frame.size.height);
[self addSubview:label];
i++;
xPos += width;
NSLog(#"%#",NSStringFromCGRect(label.frame));
}
self.messagesWidth = xPos;
self.contentSize = CGSizeMake(xPos, self.frame.size.height);
self.contentOffset = CGPointMake(-self.frame.size.width, 0.0);
}
-(void)gototest
{
NSLog(#"test %#",#"ccc ");
}
and then for marquee
- (void)go {
if (!self.period) self.period = self.messagesWidth / 100;
// so it always takes about the same (fudged, but reasonable) amount of time to scroll the whole array
[UIView animateWithDuration:self.period
delay:0.0
options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionRepeat
animations:^{
self.contentOffset = CGPointMake(self.messagesWidth, 0.0);
} completion:^(BOOL finished){}];
}
So, my goal is to create a marquee for news and each news be clickable to see the details of the clicked new.
but the UITapGestureRecognizer doesnt work and I dont know why.
Note that self is scrollview because my class extend from UIScrollView.
So please help me

You have to subclass UIScrollView in the following way. Then use your custom scroll view (i.e. TouchScrollView) instead of the regular UIScrollView
TouchScrollView.h:
#import <Foundation/Foundation.h>
#import UIKit;
#interface TouchScrollView : UIScrollView
#end
TouchScrollView.m:
#import "TouchScrollView.h"
#implementation TouchScrollView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// Respond to touch events here
// ...
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesEnded:touches withEvent:event];
}
#end
Or if you want to do it in Swift since it's the fashion nowadays:
TouchScrollView.swift
import Foundation
import UIKit
class TouchScrollView: UIScrollView {
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
// Respond to touch events here
// ...
nextResponder().touchesBegan(touches, withEvent: event)
}
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
nextResponder().touchesMoved(touches, withEvent: event)
}
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
nextResponder().touchesEnded(touches, withEvent: event)
}
}

Related

how to give action to UIImageView using the objective-c [duplicate]

I want to call my button click event while touching the uiimageview named as(image1).Imageview placed in uiview(view1).
This is my code:
- (IBAction)buttonClicked:(id)sender
{
myTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(alphabutt:) userInfo:nil repeats:NO];
}
-(IBAction)alphabutt:(id)sender
{
NSLog(#"alphabutt!");
if(i==1)
{
NSLog(#"i==1");
[image1 setImage:[UIImage imageNamed:#"appleimg.jpg"]];
[view1 addSubview:image1];
transition1 = [CATransition animation];
transition1.duration = 0.75;
transition1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition1.type=kCATransitionReveal;
transition1.subtype=kCATransitionFromRight;
transitioning = YES;
transition1.delegate = self;
[image1.layer addAnimation:transition1 forKey:nil];
}
if(i==2)
{
NSLog(#"i==2");
[image1 setImage:[UIImage imageNamed:#"boatimg.jpg"]];
[view1 addSubview:image1];
transition2 = [CATransition animation];
transition2.duration = 0.75;
transition2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition2.type=kCATransitionFade;
transition2.subtype=kCATransitionFromRight;
transitioning = YES;
transition2.delegate = self;
[image1.layer addAnimation:transition2 forKey:nil];
i=0;
}
i++;
}
The above is my code while clicking the button the buttonclicked event is called.Inside buttonclick event the timer is working to call the alphabutt(Button click event)with the time interval of 2.0.alphabutt is called with every 2.0.I want to do this animation when i'm touch the uiimageview(image1) then only it calls the button click event (alphabutt).
how can I write the touch event for imageview...
Please explain briefly with some code to perform uiimageview touch event...
you can use UITapGestureRecognizer added to the UIImageView via addGestureRecognizer
snippets:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(bannerTapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[iv addGestureRecognizer:singleTap];
[iv setUserInteractionEnabled:YES];
and
- (void)bannerTapped:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"%#", [gestureRecognizer view]);
}
Make your image view user interaction enabled in the xib (or through the code if you are adding it programmatically) and use the UIResponder methods touchesBegan, touchesMoved, touchesEnded etc to detect the touch on the image view:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == yourImageView)
{
//add your code for image touch here
}
}
Here I show you two possibilities for achieving that in Swift:
First Possibility
import UIKit
class ViewController: UIViewController {
#IBOutlet var myUIImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// You can also manually set it through IB
// selecting the UIImageView in the storyboard
// and checking "User Interaction Enabled" checkbox
// in the Attributes Inspector panel.
self.myUIImageView.userInteractionEnabled = true
}
// You can choose to use one of the UIResponder methods:
// touchesBegan, touchesMoved, touchesEnded etc, in order to detect the touch
// on the UIImageView.
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch: UITouch? = touches.first
if touch?.view == myUIImageView {
println("myUIImageView has been tapped by the user.")
}
super.touchesEnded(touches, withEvent: event)
}
}
Second Possibility
import UIKit
class ViewController: UIViewController {
#IBOutlet var myUIImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let singleTap = UITapGestureRecognizer(target: self, action: "myUIImageViewTapped:")
singleTap.numberOfTapsRequired = 1 // Initialized to 1 by default
singleTap.numberOfTouchesRequired = 1 // Initialized to 1 by default
self.myUIImageView.addGestureRecognizer(singleTap)
self.myUIImageView.userInteractionEnabled = true
}
func myUIImageViewTapped(recognizer: UITapGestureRecognizer) {
if(recognizer.state == UIGestureRecognizerState.Ended){
println("myUIImageView has been tapped by the user.")
}
}
}
You can also drag and drop a UIButton object over the UIImageView. Then in the storyboard editor, just make the UIButton of Custom type, rendering the UIButton invisible. Then handle the click event just like you would for any button.

How to disable delaysContentTouches in UITableViewController? [duplicate]

I've looked at a ton of posts on similar things, but none of them quite match or fix this issue. Since iOS 7, whenever I add a UIButton to a UITableViewCell or even to the footerview it works "fine", meaning it receives the target action, but it doesn't show the little highlight that normally happens as you tap a UIButton. It makes the UI look funky not showing the button react to touch.
I'm pretty sure this counts as a bug in iOS7, but has anyone found a solution or could help me find one :)
Edit:
I forgot to mention that it will highlight if I long hold on the button, but not a quick tap like it does if just added to a standard view.
Code:
Creating the button:
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.titleLabel.font = [UIFont systemFontOfSize:14];
button.titleLabel.textColor = [UIColor blueColor];
[button setTitle:#"Testing" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents: UIControlEventTouchDown];
button.frame = CGRectMake(0, 0, self.view.frame.size.width/2, 40);
Things I've Tested:
//Removing gesture recognizers on UITableView in case they were getting in the way.
for (UIGestureRecognizer *recognizer in self.tableView.gestureRecognizers) {
recognizer.enabled = NO;
}
//Removing gestures from the Cell
for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) {
recognizer.enabled = NO;
}
//This shows the little light touch, but this isn't the desired look
button.showsTouchWhenHighlighted = YES;
In that tableview you just add this property.
tableview.delaysContentTouches = NO;
And add in cellForRowAtIndexPath after you initiate the cell you just add below code. The structure of the cell is apparently different in iOS 6 and iOS 7.
iOS 7 we have one control UITableViewCellScrollView In between UITableViewCell and content View.
for (id obj in cell.subviews)
{
if ([NSStringFromClass([obj class]) isEqualToString:#"UITableViewCellScrollView"])
{
UIScrollView *scroll = (UIScrollView *) obj;
scroll.delaysContentTouches = NO;
break;
}
}
Since iOS 8 we need to apply the same technique to UITableView subviews (table contains a hidden UITableViewWrapperView scroll view). There is no need iterate UITableViewCell subviews anymore.
for (UIView *currentView in tableView.subviews) {
if ([currentView isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)currentView).delaysContentTouches = NO;
break;
}
}
This answer should be linked with this question.
I tried to add this to the accepted answer but it never went through. This is a much safer way of turning off the cells delaysContentTouches property as it does not look for a specific class, but rather anything that responds to the selector.
In Cell:
for (id obj in self.subviews) {
if ([obj respondsToSelector:#selector(setDelaysContentTouches:)]) {
[obj setDelaysContentTouches:NO];
}
}
In TableView:
self.tableView.delaysContentTouches = NO;
For a solution that works in both iOS7 and iOS8, create a custom UITableView subclass and custom UITableViewCell subclass.
Use this sample UITableView's initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// iterate over all the UITableView's subviews
for (id view in self.subviews)
{
// looking for a UITableViewWrapperView
if ([NSStringFromClass([view class]) isEqualToString:#"UITableViewWrapperView"])
{
// this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
Use this sample UITableViewCell's initWithStyle:reuseIdentifier:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// iterate over all the UITableViewCell's subviews
for (id view in self.subviews)
{
// looking for a UITableViewCellScrollView
if ([NSStringFromClass([view class]) isEqualToString:#"UITableViewCellScrollView"])
{
// this test is here for safety only, also there is no UITableViewCellScrollView in iOS8
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
What I did to solve the problem was a category of UIButton using the following code :
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = YES; }];
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self performSelector:#selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self performSelector:#selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)setDefault
{
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = NO; }];
}
the button reacts correctly when I press on it in a UITableViewCell, and my UITableView behaves normally as the delaysContentTouches isn't forced.
Here's Roman B's answer in Swift 2:
for view in tableView.subviews {
if view is UIScrollView {
(view as? UIScrollView)!.delaysContentTouches = false
break
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
for (id view in self.tableView.subviews)
{
// looking for a UITableViewWrapperView
if ([NSStringFromClass([view class]) isEqualToString:#"UITableViewWrapperView"])
{
// this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
I was having similar issues with a text-only UIButton in a UITableViewCell not highlighting upon touch. What fixed it for me was changing the buttonType from Custom back to System.
Setting delaysContentTouches to NO did the trick for the image-only UIButton in the same UITableViewCell.
self.tableView.delaysContentTouches = NO;
This is a Swift version of Raphaƫl Pinto's answer above. Don't forget to upvote him too :)
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = true }
}
override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
self.setDefault()
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
self.setDefault()
}
}
func setDefault() {
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = false }
}
Solution in Swift, iOS8 only (needs the extra work on each of the cells for iOS7):
//
// NoDelayTableView.swift
// DivineBiblePhone
//
// Created by Chris Hulbert on 30/03/2015.
// Copyright (c) 2015 Chris Hulbert. All rights reserved.
//
// This solves the delayed-tap issue on buttons on cells.
import UIKit
class NoDelayTableView: UITableView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delaysContentTouches = false
// This solves the iOS8 delayed-tap issue.
// http://stackoverflow.com/questions/19256996/uibutton-not-showing-highlight-on-tap-in-ios7
for view in subviews {
if let scroll = view as? UIScrollView {
scroll.delaysContentTouches = false
}
}
}
override func touchesShouldCancelInContentView(view: UIView!) -> Bool {
// So that if you tap and drag, it cancels the tap.
return true
}
}
To use, all you have to do is change the class to NoDelayTableView in your storyboard.
I can confirm that in iOS8, buttons placed inside a contentView in a cell now highlight instantly.
Slightly modified version of Chris Harrison's answer. Swift 2.3:
class HighlightButton: UIButton {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = true }
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
super.touchesCancelled(touches, withEvent: event)
setDefault()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
setDefault()
}
private func setDefault() {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = false }
}
}
}
The accepted answer did not work at some "taps" for me .
Finally I add the bellow code in a uibutton category(/subclass),and it works a hundred percent.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.backgroundColor = [UIColor greenColor];
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
self.backgroundColor = [UIColor clearColor];
} completion:^(BOOL finished)
{
}];
[super touchesBegan:touches withEvent:event];
}
I wrote a category extension on UITableViewCell to make this issue simple to address. It does basically the same thing as the accepted answer except I walk up the view hierarchy (as opposed to down) from the UITableViewCell contentView.
I considered a fully "automagic" solution that would make all cells added to a UITableView set their delaysContentTouches state to match the owning UITableView's delaysContentTouches state. To make this work I'd have to either swizzle UITableView, or require the developer to use a UITableView subclass. Not wanting to require either I settled on this solution which I feel is simpler and more flexible.
Category extension and sample harness here:
https://github.com/TomSwift/UITableViewCell-TS_delaysContentTouches
It's dead-simple to use:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// using static cells from storyboard...
UITableViewCell* cell = [super tableView: tableView cellForRowAtIndexPath: indexPath];
cell.ts_delaysContentTouches = NO;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
Here's the code for the category:
#interface UITableViewCell (TS_delaysContentTouches)
#property (nonatomic, assign) BOOL ts_delaysContentTouches;
#end
#implementation UITableViewCell (TS_delaysContentTouches)
- (UIScrollView*) ts_scrollView
{
id sv = self.contentView.superview;
while ( ![sv isKindOfClass: [UIScrollView class]] && sv != self )
{
sv = [sv superview];
}
return sv == self ? nil : sv;
}
- (void) setTs_delaysContentTouches:(BOOL)delaysContentTouches
{
[self willChangeValueForKey: #"ts_delaysContentTouches"];
[[self ts_scrollView] setDelaysContentTouches: delaysContentTouches];
[self didChangeValueForKey: #"ts_delaysContentTouches"];
}
- (BOOL) ts_delaysContentTouches
{
return [[self ts_scrollView] delaysContentTouches];
}
#end
Since objc is dynamic, and scrollView is the only class that responds to delaysContentTouches, this should work for both ios 7 and 8 (put it somewhere early in your tableViewController, like awakeFromNib):
for (id view in self.tableView.subviews)
{
if ([view respondsToSelector:#selector(delaysContentTouches)]) {
UIScrollView *scrollView = (UIScrollView *)view;
scrollView.delaysContentTouches = NO;
break;
}
}
You may also have to turn off "delaysContentTouches" in your storyboard or nib by selecting the table inside your viewController. BTW, this might not work on ios 7 if you're using a tableView inside a viewController, at least I couldn't get it to work.
That solution for me doesn't work, I fixed subclassing TableView and implementing these two methods
- (instancetype)initWithCoder:(NSCoder *)coder{
self = [super initWithCoder:coder];
if (self) {
for (id obj in self.subviews) {
if ([obj respondsToSelector:#selector(setDelaysContentTouches:)]){
[obj performSelector:#selector(setDelaysContentTouches:) withObject:NO];
}
}
}
return self;
}
- (BOOL)delaysContentTouches{
return NO;
}
Solution in Swift for iOS 7 and 8:
First I wrote a utility function:
class func classNameAsString(obj: AnyObject) -> String {
return _stdlib_getDemangledTypeName(obj).componentsSeparatedByString(".").last!
}
then I subclass UITableView and implement this:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
for view in self.subviews {
if (Utility.classNameAsString(view) == "UITableViewWrapperView") {
if view.isKindOfClass(UIScrollView) {
var scroll = (view as UIScrollView)
scroll.delaysContentTouches = false
}
break
}
}
}
I also subclass UITableViewCell and implement this:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
for view in self.subviews {
if (Utility.classNameAsString(view) == "UITableViewCellScrollView") {
if view.isKindOfClass(UIScrollView) {
var scroll = (view as UIScrollView)
scroll.delaysContentTouches = false
}
}
}
}
In my case the init(coder:) will run. Please put debug point in your init functions to know which init function will run, then using the code above to make it work.
Hope to help someone.
In Swift 3 this UIView extension can be used on the UITableViewCell. Preferably in the cellForRowAt method.
func removeTouchDelayForSubviews() {
for subview in subviews {
if let scrollView = subview as? UIScrollView {
scrollView.delaysContentTouches = false
} else {
subview.removeTouchDelayForSubviews()
}
}
}

How to check if an SKSpriteNode has been tapped

I have been trying to make a simple app where when you tap a circle, it will disappear, and a new one will come up somewhere else.
Here is all my code, edited, but it still does not work.
#import "GameScene.h"
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
[self LabelShow];
}
int Count = 0;
int CountCheck = 0;
-(void) LabelShow {
//Nothing yet
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
while (CountCheck == Count) {
int FNum1 = 350;
int TNum1 = 650;
int newx = (arc4random()%(TNum1-FNum1))+FNum1;
NSLog(#"RandomXCoord: %i", newx);
int FNum2 = 100;
int TNum2 = 700;
int newy = (arc4random()%(TNum2-FNum2))+FNum2;
NSLog(#"RandomYCoord: %i", newy);
//Location to newx and newy
CGPoint location = CGPointMake(newx, newy);
SKSpriteNode *Circle = [SKSpriteNode spriteNodeWithImageNamed:#"Circle.png"];
Circle.xScale = 0.2;
Circle.yScale = 0.2;
Circle.position = location;
//Shows chircle
[self addChild:Circle];
CountCheck++;
for (UITouch *touch in touches)
{
CGPoint locationTouch = [touch locationInNode:self];
if ([Circle containsPoint:locationTouch]) {
NSLog(#"Success!");
[Circle runAction:[SKAction fadeOutWithDuration:0]];
Count++;
}
}
}
}
#end
As I stated, I have the code that puts the circle on the screen in another method. But whenever I run this code (click the circle), the if statement at the bottom does not get executed.
I tried all the things in this thread:
Can't tap SKSpriteNode - no touch detected ios
, but I can't get it to work still, and have changed the code back to the original.
Could anyone tell me how I could be able to get the if statement executed when you tap the SKSpriteNode?
Thank you for reading this question.
Edit I changed the code, but it still doesn't work
Add the following above your Circle.xScale = 0.2; statement
Circle.name = #"Circle";
and replace your for-loop with the following
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"Circle"]) {
NSLog(#"Success!");
[node removeFromParent];
++Count;
}
}
What you need is containsPoint method.
Here is the touchesBeganWithEvent: method you are looking for:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
if ([circle containsPoint:location] {
// executed when you touch a circle
}
}
}
Note that you can add more statements and conditions.
I created a game for myself, and I had to do the same thing for a character in it. I subclassed SKNode, and added the character details (SKSpriteNode, name, SKAction, etc) in the subclassed model. Then I added the UITouchBegin method to the subclass, so my character would listen to touches.
It is a lot easier to use subclassing, as you can leave all the legwork to the subclass, and focus on the rest of your game. Adding this code to your subclass will take care of it all:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self removeFromParent];
}
You can even set your scene as your character class's delegate, and as soon as the character is removed from parentview, you can create a new one.
If you get stuck on the subclass issues, this answer really helped me out. Let me know if any questions and good luck :)
The other responses seem to detect touches on a SKSpriteNode, not a tap. This code detects tap events.
Edit TapMaxDelta to change how much movement is allowed before cancelling the tap gesture.
class TapNode : SKSpriteNode {
// Tap Vars
var firstPoint : CGPoint?
var TapMaxDelta = CGFloat(10)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
let texture = SKTexture(imageNamed: "Test.png")
super.init(texture: texture, color: UIColor.clear, size: texture.size())
isUserInteractionEnabled = true
}
// ================================================================================================
// Touch Functions
// ================================================================================================
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
firstPoint = firstTouch.location(in: self)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first, let firstPoint = firstPoint {
let curPoint = firstTouch.location(in: self)
if abs(curPoint.x - firstPoint.x) <= TapMaxDelta && abs(curPoint.y - firstPoint.y) <= TapMaxDelta {
print("tap yo")
}
}
}
}

How to deselect a segment in Segmented control button permanently till its clicked again

I have a UISegmentedControl with 4 segments. When it is selected, it should maintain the selected state. When the same segment is clicked again, it should deselect itself.
How to achieve this?
Since UISegmentedControl only sends an action if a not selected segment is selected, you have to subclass UISegmentedControl to make a tiny change in its touch handling. I use this class:
#implementation MBSegmentedControl
// this sends a value changed event even if we reselect the currently selected segment
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger current = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (current == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
#end
Now you will get UIControlEventValueChanged events even if the segment is already selected. Simply save the current index in a variable and compare it in the action. If the two indexes match you have to unselect the touched segment.
// _selectedSegmentIndex is an instance variable of the view controller
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_selectedSegmentIndex = self.segment.selectedSegmentIndex;
}
- (IBAction)segmentChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == _selectedSegmentIndex) {
NSLog(#"Segment %d deselected", sender.selectedSegmentIndex);
sender.selectedSegmentIndex = UISegmentedControlNoSegment;
_selectedSegmentIndex = UISegmentedControlNoSegment;
}
else {
NSLog(#"Segment %d selected", sender.selectedSegmentIndex);
_selectedSegmentIndex = sender.selectedSegmentIndex;
}
}
iOS 7 changed how touches are handled for UISegmentedControl. The selectedSegmentIndex is now changed during touchesEnded:.
So the updated Subclass should look like this:
#implementation MBSegmentedControl
+ (BOOL)isIOS7 {
static BOOL isIOS7 = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSInteger deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:#"."] objectAtIndex:0] integerValue];
if (deviceSystemMajorVersion >= 7) {
isIOS7 = YES;
}
else {
isIOS7 = NO;
}
});
return isIOS7;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (![[self class] isIOS7]) {
// before iOS7 the segment is selected in touchesBegan
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
// if the selectedSegmentIndex before the selection process is equal to the selectedSegmentIndex
// after the selection process the superclass won't send a UIControlEventValueChanged event.
// So we have to do this ourselves.
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
if ([[self class] isIOS7]) {
// on iOS7 the segment is selected in touchesEnded
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
#end
Swift 2.2 version, fixed the problem Grzegorz noticed.
class ReselectableSegmentedControl: UISegmentedControl {
#IBInspectable var allowReselection: Bool = true
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, withEvent: event)
if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex {
if let touch = touches.first {
let touchLocation = touch.locationInView(self)
if CGRectContainsPoint(bounds, touchLocation) {
self.sendActionsForControlEvents(.ValueChanged)
}
}
}
}
}
Swift 3.0 changes the fix for this to look like the following:
class MyDeselectableSegmentedControl: UISegmentedControl {
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let previousIndex = selectedSegmentIndex
super.touchesEnded(touches, with: event)
if previousIndex == selectedSegmentIndex {
let touchLocation = touches.first!.location(in: self)
if bounds.contains(touchLocation) {
sendActions(for: .valueChanged)
}
}
}
}
Here is a fix for a problem that when you try to cancel selection by starting tap on UISegmentControl and later you finish touch outside - it still does deselection.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if ([self pointInside:viewPoint withEvent:event]) {
int oldValue = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
if (oldValue == self.selectedSegmentIndex)
{
[super setSelectedSegmentIndex:UISegmentedControlNoSegment];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
You can do it with the following (thank you Grzegorz's answer and Matthias's answer):
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if ([self pointInside:viewPoint withEvent:event] && previousSelectedSegmentIndex == self.selectedSegmentIndex) {
self.selectedSegmentIndex = UISegmentedControlNoSegment;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
I have made an open-source (MIT Licensed) class STASegmentedControl (supports iOS 7+), that has this functionality baked in (and more).
In reference to the answer posted by #Matthias Bauch. I had to make little changes as per Swift 2.2 in Xcode 7.3:
class ReselectableSegmentedControl: UISegmentedControl {
#IBInspectable var allowReselection: Bool = true
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, withEvent: event)
if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex {
if let touch = touches.first {
let touchLocation = touch.locationInView(self)
if CGRectContainsPoint(bounds, touchLocation) {
self.sendActionsForControlEvents(.ValueChanged)
}
}
}
}
}
swift 3.1 version posted by #Kushal Ashok
class ReselectableSegmentedControl: UISegmentedControl {
#IBInspectable var allowReselection: Bool = true
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, with: event)
if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex {
if let touch = touches.first {
let touchLocation = touch.location(in: self)
if bounds.contains(touchLocation) {
self.sendActions(for: .valueChanged)
}
}
}
}
}
Here is a solution wich is independent from IOS Version. It's choosing the behaviour itself.
#interface CustomSegmentedControl : UISegmentedControl
#end
#implementation CustomSegmentedControl{
BOOL _touchBegan;
BOOL _reactOnTouchBegan;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_touchBegan = YES;
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (_reactOnTouchBegan) {
// before iOS7 the segment is selected in touchesBegan
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
_touchBegan = NO;
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
if (!_reactOnTouchBegan) {
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if ([self pointInside:viewPoint withEvent:event]) {
// on iOS7 the segment is selected in touchesEnded
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
}
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents {
if(controlEvents == UIControlEventValueChanged){
_reactOnTouchBegan = _touchBegan;
}
[super sendActionsForControlEvents:controlEvents];
}
#end
Very helpful! Thank you! I wanted a little more control over the events for my project, so I adapted #Matthias's answer to send a custom "Value unchanged" event. I put an example on GitHub.
I also incorporated #Grzegorz's fix so it behaves properly if the user drags her finger outside the segmented control.
In reference to #Stunner, this is my contribution to achieve the goal. I changed something and added the property _previousSelectedSegmentIndex; in the #Stunner code the variable previousSelectedSegmentIndex was useless:
#implementation STASegmentedControl
{
NSInteger _previousSelectedSegmentIndex;
}
- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex
{
[super setSelectedSegmentIndex: selectedSegmentIndex];
_previousSelectedSegmentIndex = self.selectedSegmentIndex;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if (self.toggleableSegments) { // toggle selected segment on/off
if ([self pointInside:viewPoint withEvent:event] && _previousSelectedSegmentIndex == self.selectedSegmentIndex) {
self.selectedSegmentIndex = UISegmentedControlNoSegment;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
_previousSelectedSegmentIndex = self.selectedSegmentIndex;
}

UIScrollview limit swipe area

I am trying to limit the swipe area of the UIScrollview, but i amnot able to do that.
I would like to set the swipe area only to the top of the UIScrollview, but i would like to set all the content visible.
Update:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count] > 0) {
UITouch *tempTouch = [touches anyObject];
CGPoint touchLocation = [tempTouch locationInView:self.categoryScrollView];
if (touchLocation.y > 280.0)
{
NSLog(#"enabled");
self.categoryScrollView.scrollEnabled = YES;
}
}
[self.categoryScrollView touchesBegan:touches withEvent:event];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// [super touchesEnded:touches withEvent:event];
self.categoryScrollView.scrollEnabled = YES;
[self.categoryScrollView touchesBegan:touches withEvent:event];
}
Solution:
dont forget to set delaysContentTouches to NO on the UIScrollView
self.categoryScrollView.delaysContentTouches = NO;
You can disable scrolling on the UIScrollView, override touchesBegan:withEvent: in your view controller, check if any of the touches began in the area where you'd like to enable swipes, and if the answer is 'yes', re-enable scrolling. Also override touchesEnded:withEvent: and touchesCancelled:withEvent: to disable scrolling when the touches are over.
Other answers didn't work for me. Subclassing UIScrollView worked for me (Swift 3):
class ScrollViewWithLimitedPan : UIScrollView {
// MARK: - UIPanGestureRecognizer Delegate Method Override -
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let locationInView = gestureRecognizer.location(in: self)
print("where are we \(locationInView.y)")
return locationInView.y > 400
}
}
This blog post showcases a very simple and clean way of implementing the functionality.
// init or viewDidLoad
UIScrollView *scrollView = (UIScrollView *)view;
_scrollViewPanGestureRecognzier = [[UIPanGestureRecognizer alloc] init];
_scrollViewPanGestureRecognzier.delegate = self;
[scrollView addGestureRecognizer:_scrollViewPanGestureRecognzier];
//
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer
{
return NO;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == _scrollViewPanGestureRecognzier)
{
CGPoint locationInView = [gestureRecognizer locationInView:self.view];
if (locationInView.y > SOME_VALUE)
{
return YES;
}
return NO;
}
return NO;
}

Resources