i tried to do something like this.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
return _scrollView;
}
return nil;
}
but i get an error like
error: Automatic Reference Counting Issue: Receiver type 'ViewController' for instance message does not declare a method with selector 'pointInside:withEvent:'
please help me,thanks
What are you trying to do with that code?
self is a UIViewController, but the pointInside:withEvent: method is defined for UIViews. Try changing it to:
if([self.view pointInside:point withEvent:event])
Based on the reference to _scrollView though, it looks like you should be writing something like:
if([self.scrollView pointInside:point withEvent:event])
Since you return the scrollview if that test passes.
To go even one step further, though, the default behaviour for hitTest:withEvent: should in fact return _scrollView if it's a subview of self.view. So you shouldn't even need to write that method unless you're specifically excluding some of the other subviews from touch events. In that case, use the self.scrollView way.
Related
In my app I have 2 transparent UIViewController layers.
the first layer contain UIView object that i am trying to recognize by touch with:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
method.
the problem is,
there is an transparent UIViewController above it.
I have tried to implement touch event on the SeconedStackedViewController and to create an instance of FirstStackedViewController and call the same method from there. the methods are called, but the hit test not.
Code:
FirstStackedViewController *fsvc = [[FirstStackedViewController alloc]init];
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
[fsvc hitTest:point withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
fsvc = [[FirstStackedViewController alloc]init];
[fsvc touchesEnded:touches withEvent:event];
}
How can I override this method to be called on the FirstStackedViewController?
if i will can simulate a touch on the FirstStackedViewController i think it will make the work
My first question is why you are pushing a transparent view controller. Might your needs be better served by layering a transparent view within the same controller?
Without looking at your requirements, it's hard to say for sure, but most likely this is more in accord with the usual separation of logic: you have a need for code that can receive input from one view and pass it to other logic. It has need to know about the internals of both, and therefore is a good candidate for a controller object with the knowledge needed to accomplish your task.
In this case, you would have no trouble at all - just present your transparent view, and pass touches to whatever action you wish.
But there are good reasons to do a VC with a transparent view. To access the VC beneath you, you can as another responder said access the app delegate. I'd keep the app delegate out of it, and just access self.navController.viewControllers. This array represents the stack of VCs. Then iterate downward - the one 'below' you is the one you want to relay the message to.
Most likely rather than using isMemberOfClass you'd want to define a protocol and check whether the VC conformsToProtocol.
for (int i=0; i < navBar.viewControllers.count ; i++) {
UIViewController *current = navBar.viewControllers[i];
if (current == self) {
UIViewController *targetVC = navBar.viewControllers[i+1]; // make sure you're not over bounds - you could do this in the loop def'n.
if ([targetVC class] conformsToProtocol("TransparentPassingActions")]) {
// pass message
}
}
}
You can also just use indexOfObject to get the current VC, I suppose, and then look one lower. That gets rid of the loop.
The pseudo-code in the previous answer confuses UIViews and UIViewControllers, by the way; perhaps this was clear to you. It is not possible to access view controllers by accessing views.
I'd start by getting a refernce to the rootViewController. I'm assuming your hierarchy is root ->1st view controller --> 2nd view controller, then by initing fsvc you are creating a new instance and you are not actually getting the reference from the view hierarchy.
I'd recommend iterating through rootviewcontroller subviews and find your instance for first viewcontroller, then call the method on that instance.
//PSEUDO-Code for class names, but iteration should solve the problem.
if(UIView *view in RootViewController.subviews){
if(view isMemberOfClass: [FirstStackedViewController class]]){
[(FirstStackedViewController *)view touchesEnded:touches withEvent:event];
}
}
//ToolUser was right about mixing up VCs and regular Views, you'd want to do this:
for(UIViewController *vc in self.navigationController.viewControllers){
if(view isMemberOfClass: [FirstStackedViewController class]]){
[(FirstStackedViewController *)vc touchesEnded:touches withEvent:event];
}
}
I want to implement a custom subclass of UIControl. It works beautifully except for one fatal problem that is making me spit teeth. Whenever I use sendActionsForControlEvents: to send an action message out, it omits to include a UIEvent. For example, if I link it to a method with the following signature:
- (IBAction) controlTouched:(id)sender withEvent:(UIEvent *)event
... the event always comes back as nil! The problem seems to occur within sendActionsForControlEvents:
Now, I need my IBAction to be able to determine the location of the touch. I usually do so by extracting the touches from the event. Surely there must be some way to ensure that the correct event is delivered? It's a pretty fundamental part of using a UIControl!
Anyone know a legal workaround?
I would assume that this is because the sendActionsForControlEvents: method can't know which UIEvent (if any) your control event should be associated with.
You could try to send all the actions separately (replicating what the sendActionsForControlEvents: method does, according to the documentation), so you can specifically associate them with a UIEvent:
UIEvent *event = ...;
UIControlEvents controlEvent = ...;
for (id target in [self allTargets]) {
NSArray *actions = [self actionsForTarget:target forControlEvent:controlEvent];
for (NSString *action in actions) {
[self sendAction:NSSelectorFromString(action) to:target forEvent:event];
}
}
I have ONE possible solution at the moment, but I'm not very happy about it. For others faced with the same problem though, here it is. First, declare a local variable or property for a UIEvent thus:
#property (nonatomic, assign) UIEvent * currentEvent;
Now, in your touch-handling routines, set that local variable to the current UIEvent for that routine before calling [self sendActionsForControlEvents:] like so, replacing UIControlEventTouchDown with whichever action you want to send out of course.
self.currentEvent = event;
[self sendActionsForControlEvents: UIControlEventTouchDown];
Finally, override the following method thus:
- (void) sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
[super sendAction:action to:target forEvent:self.currentEvent];
}
This works, but I am not in the least bit fond of it, so if anybody has an alternative solution that doesn't rely on holding a weak reference to a UIEvent, I will be overjoyed to hear it!
So I have a UIScrollView with multiple UIImageViews
I had to generate my own class of scroll view in order to tap into it's touchesEnded
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// [super touchesEnded:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
if ((int)(point.x / 100) < [[self items] count] ) {
NSLog(#" ENDED D File Selected is %# %d " , [[self items] objectAtIndex:(int)(point.x / 100) ] , (int)(point.x / 100) );
}
// [[self nextResponder] touchesEnded:touches withEvent:event];
}
items is an NSMutableArray within which I store the name of the file pointed to by each subview so basically subview[0] === item[0] etc...
subview[0] is the image View and item[0] is the fileName of that image
My question is this How can I now "advertise" alert the original caller that file xyz was selected ? As opposed to the NSLog line ?
Thanks in advance
What do you mean by "original caller"? -touchesEnded:withEvent: is called by the framework event handling subsystem.
What you might do (but don't! see below) is to add add some delegate methods of your own to the existing delegate (obviously there is already a UIScrollViewDelegate protocol and corresponding -[UIScrollView delegate] property) and call out to the delegate method you defined in lieu of your NSLog(). I discussed the refrain for doing this in a recent answer.
However, this is all moot because you are really approaching this backward and creating lots of unnecessary work for yourself. I'll preface this by saying that there are certain classes for which subclassing ought to be a trigger that you need to reconsider your design. and UIScrollView is one such class.
You have already acknowledged that you have a collection of UIImageView objects. You should let them handle interaction. The general idea would be:
1) Send each instance something like [imageView setUserInteractionEnabled:YES];. This is the single most commonly overlooked mistake when working with interactive image views.
2) Add an appropriate concrete UIGestureRecognizer instance to each image view and implement the gesture recognizer callbacks. Unless you absolutely must support ancient iOS releases, you should always try to use gesture recognizers in lieu of explicit touch handling.
3) In the gesture recognizer callbacks, add your logic code that takes an appropriate action based on the sending gesture recognizer's -view. (You could, for example, examine the corresponding view's -image or -frame and use the information to decide which image was touched.)
Since the gesture recognizer callback will likely be in your view controller it will have a much easier time "talking" to the rest of your code.
Just a small question i'm really having a lot of problems with
Basically, what I'm doing is making a view every time I hit a button which works fine.
When I want to remove all the images I made when I hit the remove from superview it just removes the last one on the stack.
Is there a way i can get rid of all the images I made?
Here is the code
This puts the picture on the screen
- (IBAction)pushBn:(id)sender {
ZeldaView *newZelda = [[ZeldaView alloc]initWithNibName:#"ZeldaView" bundle:nil];
theZeldaView=newZelda;
[self.view insertSubview:theZeldaView.view atIndex:1];
}
this removes it when i touch it
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch =[touches anyObject];
CGPoint location=[touch locationInView:theZeldaView.view];
if (CGRectContainsPoint(theZeldaView.theImage.frame, location)) {
[theZeldaView.view removeFromSuperview];
}
}
Sure, just iterate through the children of your parent view (self.view) and remove any that are ZeldaView elements.
At it's simplest this would be something like:
for (UIView* subView in [self.view.subviews])
{
if ([subView isKindOfClass:[ZeldaView class]])
[subView removeFromSuperview];
}
Though you will probably want to expand on this to not perform the actual removal during the iteration and you may want to use respondsToSelector and a custom method instead of checking the class here so that you can do any cleanup needed from within the ZeldaView class. Hopefully that makes sense to you.
sure, many ways to do this. I would keep an array of ZeldaView objects and when you want to remove them, traverse the array and remove them all.
in your .h:
#property (nonatomic, retain) NSMutableArray *zeldaViews;
when you add a ZeldaView:
// create a newZeldaView and add it to the superview
[self.zeldaViews addObject:newZeldaView];
when you want to remove them all:
for (ZeldaView *zeldaView in self.zeldaViews) {
[zeldaView.view removeFromSuperview];
}
[self.zeldaViews removeAllObjects];
create the mutable array in viewDidLoad and release it in viewDidUnload and dealloc. Modify as approp if your using ARC.
Similar to this question I have a custom subclass of UITableViewCell that has a UITextField. Its working fine except the keyboard for doesn't go away when the user touches a different table view cell or something outside the table. I'm trying to figure out the best place to find out when something outside the cell is touched, then I could call resignFirstResponder on the text field.
If the UITableViewCell could receive touch events for touches outside of its view then it could just resignFirstResponder itself but I don't see any way to get those events in the cell.
EDIT: I tried this (below) in my UITableViewCell subclass but it doesn't work, I think because touchesBegan:withEvent: doesn't get called if the event was handled by a control. I think I need to catch the events before they get send down the responder chain somehow.
The solution I'm considering is to add a touchesBegan:withEvent: method to the view controller. There I could send a resignFirstResponder to all tableview cells that are visible except the one that the touch was in (let it get the touch event and handle it itself).
Maybe something like this pseudo code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = // TBD - may need translate to cell's coordinates
for (UITableViewCell* aCell in [theTableView visibleCells]) {
if (![aCell pointInside:touchPoint withEvent:event]) {
[aCell resignFirstResponder];
}
}
}
I'm not sure if this is the best way to go about this. There doesn't seem to be any way for the tableviewcell itself to receive event notifications for events outside its view.
EDIT2: I thought I had an answer (I even posted it as an answer) using hitTest:withEvent: but that didn't work out. It doesn't always get called. :-(
[Edited: removed previous attempt which didn't always work, this one does]
OK, I finally figured a solution that fully works. I subclassed UITableView and overrode the hitTest:withEvent: method. It gets invoked for all touches anywhere in the table view, the only other possible touches are in the navbar or keyboard and the tableview's hitTest doesn't need to know about those.
This keeps track of the active cell in the table view, and whenever you tap a different cell (or non-cell) it sends a resignFirstResponder to the cell going inactive, which gives it a chance to hide its keyboard (or its datepicker).
-(UIView*) hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
// check to see if the hit is in this table view
if ([self pointInside:point withEvent:event]) {
UITableViewCell* newCell = nil;
// hit is in this table view, find out
// which cell it is in (if any)
for (UITableViewCell* aCell in self.visibleCells) {
if ([aCell pointInside:[self convertPoint:point toView:aCell] withEvent:nil]) {
newCell = aCell;
break;
}
}
// if it touched a different cell, tell the previous cell to resign
// this gives it a chance to hide the keyboard or date picker or whatever
if (newCell != activeCell) {
[activeCell resignFirstResponder];
self.activeCell = newCell; // may be nil
}
}
// return the super's hitTest result
return [super hitTest:point withEvent:event];
}
In my UITableViewCell subclasses that have a UITextField, I add the following code to get rid of the keyboard (or date picker, which slides up just like the keyboard):
-(BOOL)resignFirstResponder
{
[cTextField resignFirstResponder];
return [super resignFirstResponder];
}
Yay!
I think you're on the right track, but touchesBegan:withEvent: is a UIResponder method, so you'd actually have to override it in a UIView subclass rather than in your UIViewController subclass. Your options are:
If you're already subclassing UITableViewCell, override touchesBegan:withEvent: there.
If you're using a standard UITableViewCell, implement tableView:didSelectRowAtIndexPath in your UITableView's delegate.
That is a very good solution, the best I've found on the net. The only glitch I've discovered is that if you go from one cell with a textfield to another, the keyboard dismisses and reappears resulting in a jerky type animation.