I have a UIViewController, and I've added two subviews to its view. One subview is the view of a UIViewController. The other subview is a UITextField.
I need to dismiss the keyboard for the UITextField when the user touches the other view, but I can't figure out how to detect those events. The UIViewController's tableView catches them and breaks the UIResponder chain, so my UIViewController never hears about them. I don't want to subclass everything in the hierarchy just so I can pass the event along up the chain, so what are my options?
I should mention that I'm doing everything programmatically, no IB.
Thanks guys.
So after digging into it, I don't think there's any other way. The responder chain starts with the UIView that received the touch event, if it's not caught passes to that view's controller, then to its superview and so on.
Obviously subclassing every UIView element in a UITableView is insane overkill for this situation.
What I did was create a transparent "touch shield" view with the same frame dimensions as my table view. When the keyboard expands, I add this view over my table, and when it collapses I remove it. This allows me to intercept those touches before they hit the table.
Related
I have a main view controller which overrides
touchesBegan and plays a sound when hitting a certain screen position. I have a show segue from this view controller to another one, my custom settings view controller. However, when I slide my finger across this new one, the background view controller is still responding to my touches, as a sound is still being played.
I do not have a navigation controller implemented, and it seems like other segue types have this same issue with the touch events responding in the background. Shouldn't the first view controller not be responding to these touches after the segue? Or if I am misunderstanding, what is the proper way to stop touch events being called in the background VC?
Edit: in first view controller, when printing view.isUserInteractionEnabled, I get true despite the view controller being in the background. Not sure if this is relevant.
Edit 2: I tried adding a line in prepare for segue override self.view.isUserInteractionEnabled=false, but weirdly enough, only does this disable touch when dismissing the settings view controller.
Even more strange: if I override touchesBegan in my new view controller, it silences both the touchesMoved and touchesBegan methods in my original view controller. But if I just override touchesMoved in my new controller, the touchesMoved method in both view controllers are called.
I read that we need to override all 4 of the touch handlers and call the superclass method, which I forgot to do. But overriding all 4 methods in both these classes still cause this underlying touch event to go through.
I also would like to stick to this approach rather than a gesture recognizer. I'm assuming there has to be a proper way to handle what looks like this responder chain?
Here's a simple way to replicate: If you create a simple project with two view controllers, A and B, and have a button cause segue from A to B, and implement the touches method in VC A (and let's say print something to the console), then even after segue, the exact same thing happens. VC A is still handling touch events.
If you want to prevent touches on the previous view try presenting the next view to fullscreen.
let controller = segue.destination as! YourViewController
controller.modalPresentationStyle = .fullScreen
I'm currently developing an app for iphone (my first) and I used several UITableViewController for navigation.
I then used a subview attached to self.view.superview to get a non-scroll image at the top.
The subview is created in IB, simple UIView with an UIImageView in it.
I'm adding the subview in viewDidAppear and this functions well.
But as soon as I'm tapping a cell and the navigationController pushes the next View animated, the previous view (scrolling out of sight) becomes completely white and my subview moves animated to the center. It's only for a half second or so, because then it's gone due to the next view arriving, but it's really unnerving.
I tried removing the subview in viewWillDisappear, that removes the UIImageView, but the screen still becomes completely white.
Does anybody how to fix this?
Oh, and PS: I'm working only on the Simulator, because I have no Developer Account yet. And I cannot change everything to a ViewController because I have a deadline to meet.
You shouldn't be surprised that things go wrong when you mess with the views of view controllers that don't belong to you. Rather than using a table view controller, you should replace it with a custom UIViewController whose view acts as a container view for both the table view and the non-scrolling view above it.
I checked the documentation related to First Responder but couldn't find some sort of callback which is received when some UIView resigns from being the first responder. Let me explain my scenario.
So, I have a custom UIView (created programmatically) which becomes the first responder when invoked. Now I want to dismiss/remove this view when the user clicks on the superview or some other view. I have written the logic in super view to make it as first responder when it receives a touch event but couldn't find any callback for that custom view to dismiss it when this happens.
Taking another view point I want to get a similar behavior as of the textfield's keyboard, which is dismissed when some other view becomes the first responder.
I hope my questions context is clear.
Since UIView inherits from UIResponder, you can just override the resignFirstResponder method to update your appearance as necessary:
- (BOOL)resignFirstResponder {
if (![super resignFirstResponder])
return NO;
// dismiss self here
return YES;
}
I assume you have implemented all the other view's IBAction methods when being touched. So why not in those methods let this custom view resign first responder or even be released if you find this custom view is not nil and is not needed anymore?
I am strugling on the following task.
I am trying to control my subview from another viewController class.
What I did and does not work is this.
I inserted an object and changed it class to my second viewController class.
Then I connected its UIButton outlet to a button I have on my subview.
I then connected the buttons action to the outlet of my second view controller.
What I get when I run is this.
It all shows up well but when I try to touch the button that resides in my subview app crashes. I am only left with a worringing: "Action unavailable: The "Touch Up Inside" event of "Rounded Rect Button".
It's probably my logic that is incorrect. Thanks for help.
Well after a long research I got an answer to my problem.
As it appears I was doing everything right.
The problem is that after the initial XIB file is initialized it autoreleases all views and subviews. So to prevent from gettig your second view controller from being released implement this method
- (void)awakeFromNib {
[self retain];
}
in your view controller .m file.
This method will retain your second view controller alive and allow it to recive and respond to UI actions.
How would I go about implementing dragging and dropping a UIView from UIPopoverController into the back UIView.
This is the functionality that Pages provide in their insert media popover, where you can drag a shape out from the UIPopoverController and drop it into the main document.
I am actually confused with the pan UIGestureRecognizers and where they will be implemented.
Thanks,
Umer
According to the documentation on UIPopoverController, when the popover is presented, it is presented on a special "window". Because of this, simply adding a subview to the popover view controller's content view controller is not sufficient to be able to drag a view outside of the popover view controller's view.
The easiest solution here is to create your own window, add your drag-able view to the window when dragging occurs. Make the window visible for the duration of the drag/drop, and then release your window when complete.
As mentioned above, gesture recognizers (GR) are best suited for Drag/Drop functionality. Once the GR's state has changed to "Began" the GR will control all touches until the "Ended" or "Cancelled" state is achieved which makes it ideal for dragging views between view controllers as well as windows :)
Example:
#interface MySplitViewController : UISplitViewController {
UIView *dragView;
UIWindow *dragWindow;
}
Implementation:
NOTE we do not need to call "makeKeyAndVisible" on our window. We just need to set its "Hidden" property
From Apple in regards to the makeKeyAndVisible method:
// convenience. most apps call this to show the main window and also make it key. otherwise use view hidden property
-(void)dragBegan{
self.dragWindow = [[UIWindow alloc] initWithFrame:self.view.window.frame];
[self.dragWindow addSubview:self.dragView];
[self.dragWindow setHidden:NO];
}
Here we handle the Gesture Recognizer's "Ended" or "Cancelled" state.
NOTE: It is important to remove the window when the Drag/Drop is complete or you will lose user interactiveness with the views below.
-(void)dragEnded{
[self.dragView removeFromSuperview];
[self.dragWindow setHidden:YES];
[self.dragWindow release];
[self.view addSubview:self.dragView];
}
You have to deal with two view controllers one that's in the background called mainController one that presented using a UIPopoverViewController called popoverController. Your popoverController could add a UIPanGestureRecognizer to the views, that the user can drag. The action target of the gestureRecognizer could be a method on the popoverController.
Once the user starts a dragging operation your action method is called with the gestureRecognizer as an argument, were the state of the gestureRecognizer is UIGestureRecognizerStateBegan. You could than save the current frame of the view somewere to be able to animate it back, when the dropping fails. It might be necessary to move the view to an other superview (the window for example), because I'm not sure if UIPopoverViewController clipsToBounds its view.
As the user draggs, your action method is called over and over with the gestureRecognizer in the state UIGestureRecognizerStateChanged. Use the translationInView: method on UIPanGestureRecognizer to determine how much the user dragged and update the dragged views center/frame/transform accordingly.
Once the user lifts his finger the action method is called for a last time with the gestureRecoginzers state set to UIGestureRecognizerStateEnded. Now it's time to find out if the drag was successful. For example the popoverController could ask the mainController via delegation if there's a drop target under the views current position, if so the mainController can take action, else the popoverController would animate the dragged view back to were it came from, and add it back as a subview to it's view.
I hope this is somehow comprehensible and helpful.