A is a custom UIView
B is UIImagePickerController.view, and this UIImagePickerController.showsCameraControls = NO
A.frame = B.frame
[B addSubview:A]
Now the question is: When I tap the screen, I want A and B response the tap event at the same time. Is it possible to do that ?
This will require subclassing the UIWindow and overriding the sendEvent: method. The default implementation travels in the view hierarchy and stops after the first responder which passes the hit test. Your implementation will have to continue down the responder chain.
Related
I have multiple instances of a custom class that takes inputs from keyboard. You can think of UITextField (but they are not UITextField, they are NSObject). However, they all have a property UIControl *control.
These objects are instantiated and put into an array (orders matter), and they are put on the screen in the same order.
Scenario 1: User tabs on the first object, it becomes the first responder. User taps on another object (from the same class) and that becomes the first responder. No problem.
Scenario 2: User tabs on the first object, it becomes the first responder. User taps on the TAB button of the keyboard (iPad or iPhone or wireless keyboard), I want the next object in the array becomes the next responder. iOS picks randomly [? or with some logic not clear to me] another object which is not in the same order as I want.
Problem: Because these objects are NSObjects, how can I intercept the transition to the next object. I tried using tags or tracking who is the first responder, but the problem is, if user taps on an object out of order, it is fine - I don't want to intercept that. I only want to intercept transition from one object to anther only if it is through tapping on TAB (or Next or Return) button of keyboard.
Any idea? Thanks.
You can set your custom class to have something like this
#interface testClassButtonSub : UIButton
#property (weak,nonatomic) IBOutlet UIButton *nextButton;
#end
Then you can even use the interface builder to set which will be the next responder for when a certain action is taken. (an user presses the return when inside a textfield in your custom class)
For the return you have to declare your viewcontroller as the delegate of the specific textview.
First you set the viewcontroller header like this:
#interface RegisterViewController : UIViewController <UITextFieldDelegate>
then you set the delegates in the implementation
// Set Delegates of the Text Fields
eMail.delegate = self;
userPassword.delegate = self;
userNickname.delegate = self;
and you use this delegate method to jump to the next object
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
// Jumping code here
return NO;
}
HOWEVER in your case your textfield must be inside your object, so you have to make THAT object the delegate, and in that object's implementation jumping code add the
[thisObjectsTextfield becomeFirstResponder];
I'm sure there are a lot of reasons why someone would like to have more than one button accept touches at the same time. However, most of us only need one button to be pressed at one time (for navigation, for something to be presented modally, to present a popover, a view, etc.).
So, why would Apple set the exclusiveTouch property of UIButton to NO by default?
Very old question, but deserves clarification IMO.
Despite the very misleading method documentation from Apple a view "A" with exclusiveTouch set will prevent other views from receiving events so long as A is processing some event itself (e.g. set a button with exclusiveTouch and put a finger on it, this will prevent other views in the window from being interacted with, but interaction with them will follow the usual pattern once the finger from the exlusiveTouch-item is removed).
Another effect is preventing view A from receiving events as long as some other view is interacted with (keep a button without exclusiveTouch set pressed, and the ones with exclusiveTouch will not be able to receive events as well).
You can still set a button in your view to exclusiveTouch and interact with the others, just not at the same time, as this simple test UIViewController will prove (once the correct bindings in the IB are set for both Outlets and Actions):
#import "FTSViewController.h"
#interface FTSViewController ()
- (IBAction)button1up:(id)sender;
- (IBAction)button2up:(id)sender;
- (IBAction)button1down:(id)sender;
- (IBAction)button2down:(id)sender;
#property (nonatomic, strong) IBOutlet UIButton *button1, *button2;
#end
#implementation FTSViewController
- (IBAction)button1up:(id)sender {
NSLog(#"Button1 up");
}
- (IBAction)button2up:(id)sender {
NSLog(#"Button2 up");
}
- (IBAction)button1down:(id)sender {
NSLog(#"Button1 down");
}
- (IBAction)button2down:(id)sender {
NSLog(#"Button2 down");
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Guarantees that button 1 will not receive events *unless* it's the only receiver, as well as
// preventing other views in the hierarchy from receiving touches *as long as button1 is receiving events*
// IT DOESN'T PREVENT button2 from being pressed as long as no event is being intercepted by button1!!!
self.button1.exclusiveTouch = YES;
// This is the default. Set for clarity only
self.button2.exclusiveTouch = NO;
}
#end
In light of this, the only good reason IMHO for Apple not to set exclusiveTouch to YES for every UIView subclass is that it would have made the implementation of complex gestures a real PITA, including probably some of the gestures we are already accustomed to in composite UIView subclasses (like UIWebView), as setting selected views to exclusiveTouch=NO (like button) is faster than doing a recursive exclusiveTouch=YES on pretty much everything just to enable multitouch.
The drawback of this is that in many cases the counter intuitive behaviour of UIButtons and UITableViewCells (among others...) might introduce weird bugs and make testing more tricky (as it happened to me like... 10 minutes ago? :( ).
Hope it helps
the UIView property exclusiveTouch means the view (button) is the ONLY thing in that window that can be interacted with if it is set to YES. As stated in the docs: Setting this property to YES causes the receiver to block the delivery of touch events to other views in the same window. The default value of this property is NO.
Therefore, it is the common behavior that you might have multiple buttons or interaction controls/views in a window and want exclusiveTouch set to NO.
If you set this property to YES for any UIView subclass in a window, you can not interact with anything else in the window for as long as that property is set to YES. That means if you initialize a button with exclusiveTouch = YES, but also have a table view or another button or a scroll view or any other view that is based on interaction, it will not respond to any touches.
exclusiveTouch simply means that any view underneath your UIButton will not receive the touch events.
It's set to no by default because you typically want the view underneath to receive these events. For example, if you have a UIButton on top of a scroll view and the user wants to scroll. You want the scrollView to scroll even if they begin with their finger on the UIButton.
I was just reading release notes for iOS 5 and from this version the exclusiveTouch will be set to YES by default. So just keep in mind that it will change with the new version of iOS.
In IB I created UIView inside of a UIScrollView. File's owner for both is a UIViewController named JLViewController. The UIView is wired to class BodyClock.
The BodyClock class draws a graph inside the view. It also creates several small views which act as touch hot spots. When a hot spot is touched it displays an informative alert with a button for more detail. I need to tell JLViewController to display the detailed information. I thought I could do this by making the ViewController the HotSpot's delegate. Being as how I am creating the hot spots in the BodyClock class, I can't figure out how to set the hot spot delegate to JLViewController. I am trying to do something like this..
//Code in BodyClock
//create the hot spot
id viewController = [self nextResponder];
HelpHotSpot *helpHotSpot = [[HelpHotSpot alloc] initWithFrame:CGRectMake(start_x, melatoninHeightEnd_y, 80, 40)];
helpHotSpot.delegate = viewController;
[viewController addSubview:helpHotSpot];
[helpHotSpot release];
//Code in the HotSpot after touch and request for more info
//notify JLViewController to display the details
if ([self.delegate respondsToSelector:#selector(hotSpotMore:)]) {
[self.delegate hotSpotMore:itemDetails];
}
Everything works except that respondsToSelector fails. If I NSLog viewController or self.delegate I get...
UIScrollView: 0x7443c20; frame etc...
I was expecting JLViewController: instead of UIScrollView: so I think this is the problem.
How do I set the delegate to the ViewController for these subviews?
Is it possible, or should I be using notification instead?
This:
id viewController = [self nextResponder];
isn't doing what you think it is doing. This doesn't get a view's controller but instead looks at the chain of responders and determines which is the next one for responding to events - these are typically views or controls (not to be confused with controllers). It appears that after your hot spot view, the scoll view is next.
You'll have to get the pointer to the controller correct. Maybe in your hot spot view you could add:
IBOutlet JLViewController *viewController;
And in interface builder connect this to your view controller.
Is it possible to allow the uiimageview to be pressed and linked to another uiviewcontroller?
Yes of course but you will find things much easier if you create an invisible UIButton (of type UIButtonTypeCustom) and place it over the UIImageView. It will process a touchUpInside as simply as a button because... it is a button. You can't see it but it can still capture a touch.
Because a UIImageView is a UIView it inherits methods to handle gestures and multi-touch events. Because it is also a UIResponder it inherits useful methods like touchesBegan and family. So you can use those methods to capture events yourself, but the button is the easiest way and is much easier to do in Interface Builder if you prefer to do it that way.
In the method handling the event, the familiar pattern
PriceViewController* priceVC = [[PriceViewController alloc] init];
[self.navigationController pushViewController:priceVC animated:YES];
[priceVC release];
is the rest of what you need.
I have this app I'm working on, which on a second view asks (textfield) the name for a button to be created on first view. After specifying the name and pressing OK button, the first view pops up (as demanded) but there's no new button, although created indeed. Can I use the following code in a second view method, to "refresh" the first view before presenting itself. What's wrong with this code? Any other approach? Thank you.
-(void)initWithView:(View1Controller *)aSuperview
{
theSuperview = aSuperview;
}
- (IBAction)itemNameButton
{
...
CGRect rectang;
rectang = CGRectMake(0, 0, 320, 460);// just in case
[theSuperview.view setNeedsDisplayInRect:rectang];
...
}
You should adhere to the Model-View-Controller paradigm. Views creating buttons in other views is a bad thing in general. Instead, there should be a controller (probably a UIViewController subclass) that handles receiving the input from view 2 when the user clicks OK (via an action and a outlet to the textfield) and then tells view 1 (a custom view subclass) what to do using a defined set of methods (something like -addButtonWithTitle:(NSString *)buttonTitle). The process of adding the button itself should be fairly straight forward, something like:
- (void)addButtonWithTitle:(NSString *)buttonTitle {
UIButton *newButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // whatever type you want
newButton.titleLabel.text = buttonTitle;
[self addSubview:newButton];
newButton.center = self.center; // set your position here
}
A view controller's responsibility is to control a given view. Its responsibility is not to communicate with other controllers in order to ask them to change their views, so doing this sort of thing is usually an indication of bad design.
You should have a method in your superview's controller which adds the button to its view, and then use delegation in order to be notified by your subview when it's necessary to add the button.
For a nice and simple introduction to delegates and protocols, I found this blog post to be one of the best out there.