I am confused with this logic, please help me find a solution.
I am making a uibutton with every touch on UIview and it works in principle. But the button should not overlap with the previous button when touching a second time.
Here is the code for creating a button on a 'touch ended' event.
int const kRadius = 4;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
loop = [[MagnifierView alloc] init];
loop.viewToMagnify = self;
[loop setNeedsDisplay];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[btnCamera removeFromSuperview];
if(self.activateEditMode){
self.touchTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(addLoop)
userInfo:nil
repeats:NO];
// just create one loop and re-use it.
if(loop == nil){
loop = [[MagnifierView alloc] init];
loop.viewToMagnify = self;
}
UITouch *touch = [touches anyObject];
loop.touchPoint = [touch locationInView:self];
[loop setNeedsDisplay];
}else{
// Message
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleAction:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.touchTimer invalidate];
self.touchTimer = nil;
if(self.activateEditMode){
[self createCameraBtn];
[loop removeFromSuperview];
[self setBackgroundColor:[UIColor colorWithRed:(102/255) green:(102/255) blue:(102/255) alpha:1]];
}
}
Whenever the user is touching the view I am taking the x,y value of the view and save them into a CGPoint loop.touchPoint
and I also save the x,y values into a database to check before creating the next button against the previous x,y values which I stored in the database.
So far everything is ok.
When I am handling the previous values, I am not doing it correctly in the code.
Handling code and button creation
- (BOOL)handleOverlapping{
for (ImageInfo *img in self.profileInfo.imageInfo)
{
int xr = [img.xcord intValue] + kRadius;
int yr = [img.ycord intValue] + kRadius;
if ((([selectedXCord intValue] - kRadius) <= xr) && (([selectedYCord intValue] - kRadius) <=yr))
{
[CSNotificationView showInViewController:[(SkinViewController *)[self.superview nextResponder] navigationController]
style:CSNotificationViewStyleError message:kOVERLAPING_REDDOT_ERROE];
return false;
}
else if ((([selectedXCord intValue] - kRadius+10) <= xr) && (([selectedYCord intValue] - kRadius+10) <=yr))
{
[CSNotificationView showInViewController:[(SkinViewController *)[self.superview nextResponder] navigationController]
style:CSNotificationViewStyleError message:kOVERLAPING_REDDOT_ERROE];
return false;
}
}
return true;
}
button creation
- (void)createCameraBtn{
//[self colorOfPoint:loop.touchPoint];
selectedXCord = [NSNumber numberWithDouble:loop.touchPoint.x-12];
selectedYCord = [NSNumber numberWithDouble:loop.touchPoint.y-75];
// Check whether user focusing on monitored region.
if(![self handleOverlapping])
return;
// else if (![self red:red green:green blue:blue])
// return;
btnCamera = [UIButton buttonWithType:UIButtonTypeCustom];
btnCamera.frame = CGRectMake(loop.touchPoint.x-12, loop.touchPoint.y-75, 25, 25);
[btnCamera setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btnCamera setImage:[UIImage imageNamed:#"camera.png"] forState:UIControlStateNormal];
[btnCamera addTarget:self action:#selector(captureSkin) forControlEvents:UIControlEventTouchDown];
[self addSubview:btnCamera];
}
I think I am wrongly handling the overlap method.
In this method
1. xr,yr are the x,y values of the previous button,
2. selectedYcord,selectedXcore is the current touch position.
3. every button has width and height of 25
What I want to do here is to make sure that the second button does not overlap with previous one.
example x,top,y,bottom values.
It can create button minus 10 points with respect to the previous button for any side.
Thanks in advance.
Acutally My code is corret,i did mistake in a for loop..its checking every single time because i returned a value so it never check another button.finally i came to solution by changing code like this..
- (BOOL)handleOverlapping{
BOOL firstCondition=false;
BOOL secondCondition=false;
for (ImageInfo *img in self.profileInfo.imageInfo){
int xr = [img.xcord intValue];
int yr = [img.ycord intValue];
if (xr+12<=[selectedXCord intValue]||xr-12>=[selectedXCord intValue]){
firstCondition=true;
}
else if (yr+12<=[selectedYCord intValue]||yr-12>=[selectedYCord intValue]){
secondCondition=true;
}
else
{
[CSNotificationView showInViewController:[(SkinViewController *)[self.superview nextResponder] navigationController]
style:CSNotificationViewStyleError message:kOVERLAPING_REDDOT_ERROE];
return false;
}
}
if (firstCondition || secondCondition){
return true;
}
return true;
}
Related
I'm trying to add touch events to small view blocks drifting from screen right side to left side. The problem is layer.presentationLayer hitTest:point method returns nothing, even if I do actually tap within the bounds of the view block.
Finally, I find a workaround. But, two questions still confuse me.
The converted point does not seem to be right, how to properly use presentationLayer hitTest?
In my code snippet, UIViewAnimationOptionAllowUserInteraction is not set, why I can handle touch event anyway?
The following are the code, any help will be appreciated
viewDidLoad
CGRect bounds = [[UIScreen mainScreen] bounds];
for (int i = 0; i < 15; i++) {
ViewBlock *view = [[ViewBlock alloc] initWithFrame:CGRectMake(bounds.size.width, 200, 40, 40)];
[self.view addSubview:view];
[UIView animateWithDuration:20 delay:i * 21 options:UIViewAnimationOptionCurveLinear animations:^{
view.frame = CGRectMake(-40, 200, 40, 40);
} completion:^(BOOL finished) {
[view removeFromSuperview];
}];
}
ViewBlock.m
#implementation ViewBlock
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
CALayer *presentationLayer = self.layer.presentationLayer; //Do NOT modify presentationLayer
if (presentationLayer) {
CGPoint layer_point = [presentationLayer convertPoint:point fromLayer:self.layer.modelLayer];
if ([presentationLayer hitTest:point]) {
return self; //not worked
}
if ([presentationLayer hitTest:layer_point]) {
return self; //not worked either
}
if (CGRectContainsPoint(presentationLayer.bounds, layer_point)) {
return self; //the workaround
}
}
return [super hitTest:point withEvent:event];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
NSLog(#"touches began");
}
- (void)didMoveToSuperview{
[super didMoveToSuperview];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapTouched:)]
;
tap.cancelsTouchesInView = NO;
[self addGestureRecognizer:tap];
}
- (void)tapTouched:(UITapGestureRecognizer *)sender{
NSLog(#"touches gesture");
}
#end
Q1: The converted point does not seem to be right, how to properly use presentationLayer hitTest?
What you are missing is the parameter of hitTest: should be in the coordinate system of the receiver's super layer, so the code should be:
CGPoint superLayerP = [presentationLayer.superlayer convertPoint:layer_point fromLayer:presentationLayer];
if ([presentationLayer hitTest:superLayerP]) {
return self;
}
Q2:In my code snippet, UIViewAnimationOptionAllowUserInteraction is not set, why I can handle touch event anyway
I think you can get touch event because you override - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event and return self as result, so the event will pass to it. By default(If you don't override the hitTest:withEvent: method), you won't get touch event.
If you set UIViewAnimationOptionAllowUserInteraction option, you can get touch event on the view's final frame (CGRectMake(-40, 200, 40, 40)), but in this example, the final frame is off the screen, you can set it to CGRectMake(0, 200, 40, 40) and have a test.
I'm having a hard time finding the right documentation for how to handle touch events in order to support similar behavior to the keyboard.
What I want is a button that when I long press it, it shows a custom view controller above the button, but I want the user to be able to drag their finger to one of the other buttons (without taking their finger off the screen).
I have the button with a long press and it's custom view controller all setup and working. What I can't figure is how to support dragging from the first button over to the other button in the view controller to be able to select it.
I've tried using a subclassed UIButton where I tried this:
[self addTarget:self action:#selector(onDragOver:) forControlEvents:UIControlEventTouchDragEnter];
But that doesn't work.
I also found this question How to track button selection after long press? which is precisely the functionality I'm trying to duplicate. But there are no answers.
Here's my solution. The trick is you have to use hitTest:.
First you add a gesture recognizer to the button that is a normal button - the button that you want to open a context menu / custom view controller.
Then in your gesture recognizer callback, you use hitTest: to figure out if the user is over a custom button of yours and update it's state manually.
- (id) init {
//add a long press gesture recognizer
UILongPressureGestureRecognizer * gesture = [[UILongPressureGestureRecognizer alloc] initWithTarget:self action:#selector(onLongTap:)];
[self.myButton addGestureRecognizer:gesture];
}
- (void) onLongTap:(UIGestureRecognizer *) gesture {
if(gesture.state == UIGestureRecognizerStateBegan) {
//display your view controller / context menu over the button
}
if(gesture.state == UIGestureRecognizerStateEnded) {
//gesture stopped, use hitTest to find if their finger was over a context button
CGPoint location = [gesture locationInView:self.view];
CGPoint superviewLocation = [self.view.superview convertPoint:location fromView:self.view];
UIView * view = [self.view.superview hitTest:superviewLocation withEvent:nil];
if([view isKindOfClass:[MMContextMenuButton class]]) {
//their finger was over my custom button, tell the button to send actions
MMContextMenuButton * button = (MMContextMenuButton *) view;
[self hideAndSendControlEvents:UIControlEventTouchUpInside];
if(self.draggedContextMenuButton == button) {
self.draggedContextMenuButton = nil;
}
}
if(self.draggedContextMenuButton) {
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
}
self.draggedContextMenuButton = nil;
}
if(gesture.state == UIGestureRecognizerStateChanged) {
//gesture changed, use hitTest to see if their finger
//is over a button. Manually have to tell the button
//that it should update it's state.
CGPoint location = [gesture locationInView:self.view];
CGPoint superviewLocation = [self.view.superview convertPoint:location fromView:self.view];
UIView * view = [self.view.superview hitTest:superviewLocation withEvent:nil];
if([view isKindOfClass[MMContextMenuButton class]]) {
MMContextMenuButton * button = (MMContextMenuButton *) view;
if(self.draggedContextMenuButton != button) {
[self.draggedContextMenuButton dragOut];
}
self.draggedContextMenuButton = button;
[button dragOver];
}
}
}
//////////////
#import "MMContextMenuButton.h"
#import "MMContextMenus.h"
#implementation MMContextMenuButton
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
self.layer.cornerRadius = 4;
self.adjustsImageWhenHighlighted = FALSE;
self.adjustsImageWhenDisabled = FALSE;
self.backgroundColor = [UIColor clearColor];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[self setTitleColor:[UIColor colorWithRed:0.435 green:0.745 blue:0.867 alpha:1] forState:UIControlStateNormal];
[self addTarget:self action:#selector(onHighlight:) forControlEvents:UIControlEventTouchDown];
[self addTarget:self action:#selector(onRelease:) forControlEvents:UIControlEventTouchUpOutside&UIControlEventTouchUpOutside];
return self;
}
- (void) onHighlight:(id) sender {
self.backgroundColor = [UIColor colorWithRed:0.435 green:0.745 blue:0.867 alpha:1];
}
- (void) onRelease:(id) sender {
self.backgroundColor = [UIColor clearColor];
}
- (void) hideAndSendControlEvents:(UIControlEvents) events {
[self dragOut];
[self sendActionsForControlEvents:events];
[[MMContextMenus instance] hideContextMenus];
}
- (void) dragOver {
self.highlighted = TRUE;
self.backgroundColor = [UIColor colorWithRed:0.435 green:0.745 blue:0.867 alpha:1];
}
- (void) dragOut {
self.highlighted = FALSE;
self.backgroundColor = [UIColor clearColor];
}
#end
How can I detect the selected state of a uibutton?
I have 7 buttons and I have made them to be able to toggle or select multiple buttons at a time.
I want to be able to tell which buttons are in a selected state when I push the done button.
So if M, T and W are selected then I want to be able to detect that when pushing done.
I currently put a tag on the button and then call a method to unselect or select multiple buttons.
self.repeatOccurrenceFrequencyWeeklyTF = [[UITextField alloc]init];
self.repeatOccurrenceFrequencyWeeklyTF.frame = CGRectMake(80, 80, 32, 32);
self.repeatOccurrenceFrequencyWeeklyTF.delegate = self;
self.repeatOccurrenceFrequencyWeeklyTF.background = [UIImage imageNamed:#"repeatWeekly"];
self.repeatOccurrenceFrequencyWeeklyTF.font = [UIFont fontWithName:#"SegoeWP" size:15];
self.repeatOccurrenceFrequencyWeeklyTF.textColor = [UIColor appGreyText];
[self.repeatOccurrenceFrequencyWeeklyTF setValue:[UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1.0] forKeyPath:#"_placeholderLabel.textColor"];
self.repeatOccurrenceFrequencyWeeklyTF.placeholder = #"1";
UIView *leftView1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 12, self.repeatOccurrenceFrequencyWeeklyTF.frame.size.height)];
self.repeatOccurrenceFrequencyWeeklyTF.leftView = leftView1;
self.repeatOccurrenceFrequencyWeeklyTF.leftViewMode = UITextFieldViewModeAlways;
self.repeatOccurrenceFrequencyWeeklyTF.rightViewMode = UITextFieldViewModeAlways;
self.keyboardToolbar = [self createInputToolbar];
self.repeatOccurrenceFrequencyWeeklyTF.inputAccessoryView = self.keyboardToolbar;
self.repeatOccurrenceFrequencyWeeklyTF.delegate = self;
self.repeatOccurrenceFrequencyWeeklyTF.keyboardType = UIKeyboardTypeNumberPad;
self.repeatOccurrenceFrequencyWeeklyTF.enabled = NO;
[self.view addSubview:self.repeatOccurrenceFrequencyWeeklyTF];
// Now, in your button action handler, you can do something like this:
- (void)mondayButtonTouch:(UIButton *)aButton withEvent:(UIEvent *)event
{
aButton.selected = !aButton.selected;
if(aButton.tag == 111) {
}
if(aButton.tag == 222) {
}
if(aButton.tag == 333) {
}
if(aButton.tag == 444) {
}
if(aButton.tag == 555) {
}
if(aButton.tag == 666) {
}
NSLog(#"dsfdfdfsdfs %ld", (long)aButton.tag);
[aButton setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
}
I would use a NS_ENUM (which helps to keep a nice and readable code) and a NSMutableArray to keep track of your selected buttons.
Declare a enum that looks something like this:
typedef NS_ENUM(NSInteger, Weekday) {
WeekdayMonday,
WeekdayTuesday,
WeekdayWednesday,
WeekdayThursday,
WeekdayFriday,
WeekdaySaturday,
WeekdaySunday
};
Then tag your buttons with the correct enum:
tuesdayButton.tag = WeekdayTuesday;
And check when you tap button if your enum exists in your array:
- (void)buttonTouch:(UIButton *)aButton withEvent:(UIEvent *)event
{
if ([array containsObject:#(aButton.tag)]){ //exists, remove it from array
[array removeObjectIdenticalTo:#(aButton.tag)];
}
}else{
[array addObject:#(aButton.tag)];
}
}
A possibility is to create an NSMutableArray named selectedButton. Do like this:
- (void)mondayButtonTouch:(UIButton *)aButton withEvent:(UIEvent *)event
{
aButton.selected = !aButton.selected;
if(!aButton.selected && selectedButton.containsObject(aButton.Tag)) {
[selectedButton removeObject:aButton.tag];
}
else if(aButton.selected && !selectedButton.containsObject(aButton.Tag)) {
[selectedButton addObject:aButton.tag];
}
// do your stuff here
}
Now on done button click you have all the button thats tags are selected will be trackable with selectedButton array.
You can use:
[self.view viewWithTag:yourTagHere]
you can use this :
for (UIButton *btn in [self.view subviews]) { // self.view (change it with your button superview)
if ([btn isKindOfClass:[UIButton class]] && [btn isSelected] == YES) {
// here you found the button which is selected
}
}
[self.view viewWithTag:yourTagHere] replace
I'm relativity new to Xcode and I was making a small just-for-fun kinda game for the iPhone and I'm having some trouble as far as making a certain sprite disappear when it spawns.
Brief:
So basically I have a sprite spawning on screen each time every 0.70 seconds and that sprite is set to a global IBOutlet variable called "Circle". These circles spawn at random locations around the screen of the iPhone at random widths and heights.
Overall
So basically I'm trying to make it so that when you click on the exact circle, it only hides that ball.
Thanks. Also, sorry to the mods for the sloppy formatting.
Here's the code in file that's relevant:
GameController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.gameState = GameStatePaused;
circleVerlocity = CGPointMake(CircleSpeedX, CircleSpeedY);
[NSTimer scheduledTimerWithTimeInterval:SpawnSpeed target:self selector:#selector(addCircle:) userInfo:nil repeats:YES];
[NSTimer scheduledTimerWithTimeInterval:BallSpeed target:self selector:#selector(gameLoop) userInfo:nil repeats:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)addCircle: (NSTimer *) aTimer {
if(gameState == GameStateRunning)
{
UIImageView *Circle1 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"sprite-small-1.png"]];
self.Circle = Circle1;
CGRect rect = CGRectMake(arc4random() % (274), arc4random() % (532), 50, 50);
[Circle1 setFrame:rect];
[self.view addSubview:Circle1];
}
}
-(void)gameLoop
{
if(gameState == GameStateRunning)
{
Circle.center = CGPointMake(Circle.center.x + circleVerlocity.x, Circle.center.y + circleVerlocity.y);
if(Circle.center.x > self.view.bounds.size.width || Circle.center.x < 0)
{
circleVerlocity.x = -circleVerlocity.x;
}
if(Circle.center.y > self.view.bounds.size.height || Circle.center.y < 0)
{
circleVerlocity.y = -circleVerlocity.y;
}
}else{
if(tapToBegin.hidden)
{
tapToBegin.hidden = NO;
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if(gameState == GameStatePaused)
{
tapToBegin.hidden = YES;
gameState = GameStateRunning;
}
if([touch view] == Circle)
{
Circle.hidden = YES;
}
}
You could use GestureRecognizer in your circle. with class UITapGestureRecognizer you could add a recognizer to an element,
circle.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideThatBall)];
[circle addGestureRecognizer:tapGesture];
-(void)hideThatBall{
circle.hidden = YES;
}
I would use something like this
i have read about UITouch and UIGestureRecognizer, but i still really confused what the difference between them.
I have one case. In my app, i have a barbuttonitem, i want to show different view when a tap that button. if i do a singletap, i want to show a textview, but when i do a singletap again, i want to show a popover from that button. is there somebody can give me an example code to do it and give a little explanation about what is the difference between UITouch and UIGestureRecognizer???
UPDATE
the barbuttonitem is a wrapper of UISegmentedControl, here's a pic from the desain
i was try to use touchesBegan:withEvent: and touchesEnded:withEvent: to solve this problem, but i dont know how to connect it to that barbuttonitem.
This is the code i made :
-(void)addSegment{
NSAutoreleasePool *pool;
int count_doc = [_docsegmentmodels count];
NSLog(#"count doc add segment : %d", count_doc);
pool = [[NSAutoreleasePool alloc] init];
DocSegmentedModel *sl;
NSMutableArray *segmentTextMutable = [NSMutableArray array];
for(int i=0 ;(i<count_doc && i < max_segment);i++){
sl = [_docsegmentmodels objectAtIndex:i];
NSString *evalString = [[KoderAppDelegate sharedAppDelegate] setStringWithLength:sl.docSegmentFileName:10];
[segmentTextMutable addObject:NSLocalizedString(evalString,#"")];
}
NSArray *segmentText = [segmentTextMutable copy];
_docSegmentedControl = [[UISegmentedControl alloc] initWithItems:segmentText];
_docSegmentedControl.selectedSegmentIndex = 0;
_docSegmentedControl.autoresizingMask = UIViewAutoresizingFlexibleHeight;
_docSegmentedControl.segmentedControlStyle = UISegmentedControlStyleBezeled;//UISegmentedControlStylePlain;// UISegmentedControlStyleBar;//UISegmentedControlStyleBezeled;
//docSegmentedControl.frame = CGRectMake(0, 0, 800, 46.0);
[_docSegmentedControl addTarget:self action:#selector(docSegmentAction:) forControlEvents:UIControlEventValueChanged];
// Add the control to the navigation bar
//UIBarButtonItem *segmentItem = [[UIBarButtonItem alloc] initWithCustomView:_docSegmentedControl];
segmentItem = [[UIBarButtonItem alloc] initWithCustomView:_docSegmentedControl];
self.navItem.leftBarButtonItem = segmentItem;
self.navItem.leftBarButtonItem.title = #"";
[pool release];
[segmentItem release];
[_docSegmentedControl release];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(segmentItemTapped:) object:segmentItem];
}
-(void)touchesEnd:(NSSet *)touches withEvent:(UIEvent *)event{
if (touches.count == 1) {
if (theTouch.tapCount == 2) {
[self performSelector:#selector(segmentItemTapped:)withObject:segmentItem afterDelay:0.35];
}else {
[self performSelector:#selector(docSegmentAction:) withObject:segmentItem afterDelay:0.0];
}
}
}
- (IBAction)segmentItemTapped:(id)sender{
if (self.fileProperties == nil) {
self.fileProperties = [[[FilePropertiesViewController alloc] initWithNibName:#"FilePropertiesViewController" bundle:nil]autorelease];
fileProperties.delegate = self;
self.filePropertiesPopover = [[[UIPopoverController alloc] initWithContentViewController:fileProperties]autorelease];
[_docsegmentmodels objectAtIndex:_docSegmentedControl.selectedSegmentIndex];
}
fileProperties.docProperties = _docsegmentmodels;
fileProperties.index = _docSegmentedControl.selectedSegmentIndex; //index utk nilai2 di textfield
[self.filePropertiesPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
- (IBAction)docSegmentAction:(id)sender{
NSLog(#"open file");
isFileOpen = YES;
[self.moreFilePopOverController dismissPopoverAnimated:YES];
[self.filePropertiesPopover dismissPopoverAnimated:YES];
[self showTextView];
}
is there any mistake in my understanding?
In your case you don't need UIGestureRecognizer. Simply set the action of the barbuttonitem to the method that should be called when the button is tapped. Have the method keep track of the state of the textview and depending on it show it or show the popover.