Is it possible to check number of objects in a certain touch location?
I have tagged all objects with a number, but couldn't think of a way it would work.
Basically what I want to do is add uiimageview to touch point, BUT when there is already an other uiimageview, I would do nothing.
Thanks!
You've got a good idea to start. All the objects that you add on a view are already kept in an array called [myview subviews]. It was a good idea to tag them because then you can easily access them with [myview viewWithTag: kFirstViewTag].
So to answer the second part, when your checking touch locations, check if the touch location intersects with any of your subviews.
For example:
for (UIView* view in [myView subviews]) {
if (CGRectContainsPoint([view frame], touchPoint) {
//do something
}
}
I can assume you probably don't need to go trough all the subviews, so you can just cycle to ones limited with tags from kFirstViewTag to kLastViewTag with for loop, like:
for (int i = kFirstViewTag; i <= kLastViewTag; i++) {
UIView *view = [myView viewWithTag: i];
if (CGRectContainsPoint([view frame], touchPoint) {
//do something
}
}
Related
I am building an iphone app that has multiple UIViews in a 3x3 matrix (so total of 9 UIViews) within one UIViewController. I am trying to find a way to let the user move one view to a location in the matrix which will then rearrange the rest of the views accordingly. Think of when you drag an app on your springboard to another place and all the other icons arrange themselves accordingly.
What is the best way to accomplish something like that?
Use UIPanGestureRecognizer, using the translationInView to adjust the coordinates of the item you're dragging. For a discussion of gesture recognizers, see the Event Handling Guide for iOS.
When you let go (i.e. the gesture ends), you can use UIView class method animateWithDuration to animate the moving of various items to their final locations.
So, in viewDidLoad, you might do something like the following (assuming that you had your nine controls in an array called arrayOfViews):
for (UIView *subview in self.arrayOfViews)
{
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(movePiece:)];
[subview addGestureRecognizer:gesture];
}
And then your gesture recognizer handler might look like:
- (void)movePiece:(UIPanGestureRecognizer *)gesture
{
static CGPoint originalCenter;
if (gesture.state == UIGestureRecognizerStateBegan)
{
originalCenter = gesture.view.center;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
CGPoint translation = [gesture translationInView:self.view];
gesture.view.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
[UIView animateWithDuration:0.25
animations:^{
// move your views to their final resting places here
}];
}
}
This is the bare-bones of what a dragging of controls around might look like.
If you are developing for iOS 6, definitely have a look at the UICollectionView class. If you have dealt with table views before the learning curve should not be too steep. You get the rearranging as well as all the animation for free.
I'm making an app where I have a background view and that has six UIImageView's as subviews. I have a UITapGestureRecognizer to see when one of the UIImageViews is tapped on and thie handleTap method below is what the gesture recognizer calls. However, when I run this, the hitTest:withEvent: always returns the background view even when I tap on one of the imageViews. Does it have something to do with the event when I call hitTest?
Thanks
- (void) handleTap: (UITapGestureRecognizer *) sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [sender locationInView: sender.view];
UIView * viewHit = [sender.view hitTest:location withEvent:NULL];
NSLog(#"%#", [viewHit class]);
if (viewHit == sender.view) {}
else if ([viewHit isKindOfClass:[UIImageView class]])
{
[self imageViewTapped: viewHit];
NSLog(#"ImageViewTapped!");
}
}
}
UIImageView are, by default, configured to not register user interaction.
From the UIImageView documentation:
New image view objects are configured to disregard user events by
default. If you want to handle events in a custom subclass of
UIImageView, you must explicitly change the value of the
userInteractionEnabled property to YES after initializing the object.
So, right after you initialize your views you should have:
view.userInteractionEnabled = YES;
This will turn the interaction back on and you should be able to register touch events.
There's a rewrite on your approach (single GR on the containing view) that works, but it'll make our brain hurt getting the coordinate systems right, which is definitely the problem in the posted code.
The better answer is to attach N gesture recognizers to each of the UIImageViews. They can all have the same target and use the same handleTap: method. The handleTap: can get the view without searching any geometry like this:
UIImageView *viewHit = (UIImageView *)sender.view;
I have a UIViewController that is detecting touch events with touchesBegan. There are moving UIImageView objects that float around the screen, and I need to see if the touch event landed on one of them. What I am trying:
UITouch* touch = [touches anyObject];
if ([arrayOfUIImageViewsOnScreen containsObject: [touch view]]) {
NSLog(#"UIImageView Touched!");
}
But this never happens. Also if I were to do something like this:
int h = [touch view].bounds.size.height;
NSLog([NSString stringWithFormat: #"%d", h]);
it outputs the height of the entire UIViewController (screen) everytime, even if I touch one of the UIImageViews, so clearly [touch view] is not giving me the UIImageView. How do I detect when only a UIImageView is pressed? Please do not suggest using UIButtons.
Thank you!
If you only want to detect when a UIImageView is pressed, check the class:
if (touch.view.class == [UIImageView class]) {
//do whatever
}
else {
//isnt a UIImageView so do whatever else
}
Edit----
You haven't set the userInteraction to enabled for the UIImageView have you?!
I know you said please do not suggest using UIButtons, but buttons sound like the best/easiest way to me.
You could try sending the hitTest message to the main view's CALayer with one of the touches - it'll return the CALayer furthest down the subview hierarchy that you touched.
You could then test to see if the layer you touched is a layer of one of the UIImageView's, and proceed from there.
This code uses a point generated from a UIGestureRecognizer.
CGPoint thePoint = [r locationInView:self.view];
thePoint = [self.view.layer convertPoint:thePoint toLayer:self.view.layer.superlayer];
selectedLayer = [self.view.layer hitTest:thePoint];
If you want to check the touch means use CGRectContainsPoint.
1.Capture the touch event and get the point where you touched,
2.Make a CGRect which bounds the object you want to check the touch event,
3.Use CGRectContainsPoint(CGRect , CGPoint) and catch the boolean return value.
http://developer.apple.com/library/ios/#DOCUMENTATION/GraphicsImaging/Reference/CGGeometry/Reference/reference.html
Here is the class reference for CGRect.
Forgot about this question- the problem was that I did not wait until viewDidLoad to set userInteractionEnabled on my UIImageView.
How can I position a button on my view dynamically?
Try this code
for(UIView * view in self.view.subviews)
{
if([view isKindOfClass:[UIButton class]])
{
// view is button
}
}
You can also use tags
Set the tag property of the UIButton (e.g. in interface builder, or programatically). Then you can walk the children of the owning view:
for (UIView *view in myView.subviews) {
if (view.tag == 101 /*or whatever you set it to*/) {
// things...
}
}
Ok, now we understand the question better...
You can place a button/UIComponent at runtime by settings its frame. This will place it at a given coordinate within the view containing the button.
myButton.frame = CGRectMake(newX, newY, myButton.frame.width, myButton.frame.height);
If you wish to do a linear style animation (no curves), look up CATransitions, see iPhone UIView Animation Best Practice. If you need to support pre-iOS4 devices, you can't use CATransitions, and have to do it the old way using UIView animations.
To do more complex animations, you might have to mess around with timers, which can slightly juddery effects if you're trying to do complicated animation paths.
Get a array of all the subviews of a view using:
[myView subviews]
iterate through that array and check for your butotn by some unique characteristic. (You can set the button's tag attribute to some unique value and check for that value)
What I want to do is ideally loop through all buttons in a UIView (I have a lot, over 40 buttons), and depending on the tag, change the image that the button is displaying. Does anyone know how I can do this?
Thanks!!!!
I would say use the subviews property, as devilaether said, but do an additional check to make sure the subview is a UIButton before you do anything else with it:
for(UIView *view in [rootView subviews]) {
if([view isKindOfClass:[UIButton class]]) {
if([view tag] == 0) {
// First image
} /* ... */
else {
NSLog(#"didn't recognize tag");
}
} else {
NSLog(#"view is not a button");
}
}
You could also make your life easier if you stored an NSArray somewhere with the UIButtons you wanted to iterate over; this would take out the isKindOfClass: check. See the NSObject protocol for more info.
Use the subview property of the UIView containing the buttons. For each UIView element in that NSArray, inspect the tag property. If the tag matches what your logic needs, change the image displayed in that UIView instance - which in this case is one of the buttons.
for(i=0; i<numberOfTags; i++){
UIButton *tempBtn = [yourView viewWithTag:i];
[tempBtn setImage:yourImage];
}