UISwitch value change event is not triggered iOS - ios

I am adding the UISwitch programmatically in a scroll view.
UIVIew -> UIScrollView -> UISwitch
UISwitch *toggleSwitch = [[UISwitch alloc] initWithFrame: CGRectZero];
[toggleSwitch addTarget:self action:#selector(flipMode:) forControlEvents:UIControlEventValueChanged];
toggleSwitch.on = YES;
toggleSwitch.userInteractionEnabled = YES;
[scrollView addSubview: toggleSwitch];
Action method:
- (IBAction)flipMode:(id)sender{
if([sender isOn])
{
// On Toggle ON
} else {
//On Toggle OFF
}
}
When we toggle the switch multiple times or on dragging or moving the switch slowly from ON to OFF state or vice versa at some point of time the action is not getting called. In next moment on value change it will trigger the event.
Occurrence of this issue is 2 out of 15-20 trials. Unable to find the root cause for it.
Help appreciated

set your delaysContentTouches property of scroll view to NO
yourScrollView.delaysContentTouches = NO;
This will cause your switch to "get" the touches immediately, rather
than have them go to the UIScrollView first.
Check this answer.
your flipMethod: should look like below code
-(void)flipMode:(id)sender
{
if([sender isOn])
[toggleSwitch setOn:YES animated:YES];
else
[toggleSwitch setOn:NO animated:YES];
}

Related

Highlighting a button based on its tag?

I have a UIViewController with a bunch of buttons that each have a (unique) tag. I wrote the following method:
- (void) highlightButtonWithTag: (NSInteger) tag
{
UIButton *btn = (UIButton *)[self.view viewWithTag: tag];
btn.highlighted = YES;
}
What I am trying to do is have a bunch of buttons that each function like a toggle: when I tap one, it should be come active (i.e. highlighted) and the one that was highlighted before should become "un"highlighted.
When the view comes up, I use the viewDidAppear method to set the initial selection:
- (void) viewDidAppear:(BOOL)animated
{
self.selectedIcon = 1;
[self highlightButtonWithTag: self.selectedIcon];
}
And this seems to work just fine: when the view comes up, the first button is selected. However, when I try to update stuff through the #selector connected to the buttons, the previous button is "un"highlighted but the button with sender.tag doesn't get highlighted.
- (IBAction) selectIcon:(UIButton *)sender
{
// "Un"highlight previous button
UIButton *prevButton = (UIButton *)[self.view viewWithTag: self.selectedIcon];
prevButton.highlighted = NO;
// Highlight tapped button:
self.selectedIcon = sender.tag;
[self highlightButtonWithTag: self.selectedIcon];
}
What am I missing here?
The problem is that the system automatically highlights then unhighlights the button on touchDown and touchUp respectively. So, you need to highlight the button again, after it's unhighlighted by the system. You can do by using performSelector:withObject:afterDelay: even with a 0 delay (because the selector is scheduled on the run loop which happens after the system has done it's unhighlighting). To use that method, you have to pass an object (not an integer), so If you modify your code slightly to use NSNumbers, it would look like this,
- (void) highlightButtonWithTag:(NSNumber *) tag {
UIButton *btn = (UIButton *)[self.view viewWithTag:tag.integerValue];
btn.highlighted = YES;
}
- (void) viewDidAppear:(BOOL)animated {
self.selectedIcon = 1;
[self highlightButtonWithTag: #(self.selectedIcon)];
}
- (IBAction) selectIcon:(UIButton *)sender {
// "Un"highlight previous button
UIButton *prevButton = (UIButton *)[self.view viewWithTag: self.selectedIcon];
prevButton.highlighted = NO;
// Highlight tapped button:
self.selectedIcon = sender.tag;
[self performSelector:#selector(highlightButtonWithTag:) withObject:#(self.selectedIcon) afterDelay:0];
}

UITableViewCell skipped in responder chain

I'm attempting to trigger an event in a subview of a UITableViewCell, and let it bubble up the responder chain and be handled by a custom UITableViewCell subclass.
Basically:
SomeView.m (which is a subview of the UITableViewCell)
[self.button addTarget:nil action:#selector(someAction:) events:UIControlEventTouchUpInside]
SomeCustomCell.m
- (void)someAction:(id)sender {
NSLog(#"cool, the event bubbled up to the cell");
}
And to test why this wasn't working, I've added the someAction: method on the ViewController and the ViewController is the one that ends up handling the event that bubbles up from the table view cell subview, even though the Cell should handle it. I've checked that the Cell is on the responder chain and I've verified that any views on the responder chain both above and below the cell will respond to the event if they implement the someAction: method.
What the heck is going on here?
Here's a project that shows it https://github.com/keithnorm/ResponderChainTest Is this expected behavior somehow? I haven't found any documentation stating UITableViewCell's are treated any differently than other UIResponder's.
The cell seems to ask its table view for permission. To change that you can of course override
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return [self respondsToSelector:action];
}
Swift 3, 4, 5:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return self.responds(to: action)
}
I've concluded that this is either a bug or undocumented intended behavior. At any rate, I ended up brute force fixing it by responding to the event in a subview and then manually propagating the message up the responder chain. Something like:
- (void)customEventFired:(id)sender {
UIResponder *nextResponder = self.nextResponder;
while (nextResponder) {
if ([nextResponder respondsToSelector:#selector(customEventFired:)]) {
[nextResponder performSelector:#selector(customEventFired:) withObject:sender];
break;
}
nextResponder = nextResponder.nextResponder;
}
}
I've also updated my demo project to show how I'm using this "fix" https://github.com/keithnorm/ResponderChainTest.
I still welcome any other ideas if anyone else figures this out, but this is the best I've got for now.
You can change the code in View.m as
[button addTarget:nil action:#selector(customEventFired:) forControlEvents:(1 << 24)];
to
[button addTarget:cell action:#selector(customEventFired:) forControlEvents:(1 << 24)];
do like this
#implementation ContentView
// uncomment this to see event caught by the cell's subview
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:#"Click" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blueColor]];
[button addTarget:self action:#selector(customEventFired:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(4, 5, 100, 44);
[self addSubview:button];
}
return self;
}
- (void)customEventFired:(id)sender
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Event Triggered in cell subview" message:#"so far so good..." delegate:nil cancelButtonTitle:#"cancel" otherButtonTitles:nil, nil];
[alertView show];
}
#end
now customEventFired: method get called
I think this one is the easiest solution. If you do not specify a target the event will automatically bubble up the responder chain.
[[UIApplication sharedApplication]sendAction:#selector(customAction:) to:nil from:self forEvent:UIEventTypeTouches];

iOS 7: How to hide the DONE button on the keyboard

I'm trying to give my users a way to dismiss the keyboard, whether by clicking outside the keyboard or by having a DONE button on the keyboard itself.
I have created a Done button and it works fine on iOS 6:
UIToolbar *keyboardToolbar;
keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height - 44, 320, 44)];
UIBarButtonItem *flexItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"dismiss_keyboard", nil) style:UIBarButtonItemStyleDone target:self action:#selector(dismissKeyboard)];
NSArray *items = [NSArray arrayWithObjects:flexItem,doneItem, nil];
[keyboardToolbar setItems:items animated:YES];
for (UIView *subview in [searchBar subviews])
{
if( [subview isKindOfClass:[UITextField class]] )
{
((UITextField*)subview).delegate=self;
((UITextField*)subview).inputAccessoryView = keyboardToolbar;
break;
}
}
But on iOS 7 this button is no where to be found.
I also tried using the method where the user can click anywhere but the keyboard and make it disappear:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
//Code to dismiss keyboard.
}
But my view contains a UISearchBar and a UITableView but the touchesBegan event doesn't fire when I touch those, only when I touch the parent UIView, which is not visible because it's covered by my UISearchBar and my UITableView. I have to touch the tiny space in between the two to fire the event.
How can I make my touchesBegan method apply to any object on the screen? And why is my DONE button not showing up in iOS 7?
Why is my DONE button not showing up in iOS 7?
Your DONE button isn't showing up because the internal structure of UISearchBar, which you aren't supposed to modify, has changed. (This is the reason you're not supposed to modify it.)
If you want to continue this non-recommended behavior and get it working, instead of checking if it's a UITextField, you can try checking if it conforms to UITextInputTraits, and iterate through subviews of subviews:
for(UIView *subView in [searchBar subviews]) {
if([subView conformsToProtocol:#protocol(UITextInputTraits)]) {
// iOS 6
[(UITextField *)subView setReturnKeyType: UIReturnKeyDone];
} else {
// iOS 7
for(UIView *subSubView in [subView subviews]) {
if([subSubView conformsToProtocol:#protocol(UITextInputTraits)]) {
[(UITextField *)subSubView setReturnKeyType: UIReturnKeyDone];
}
}
}
(This code is from this SO answer.)
However, this approach is not recommended, because it may break again in iOS 7.1. It also could be more resilient as a recursive method.
How can I make my touchesBegan method apply to any object on the screen?
Touch events are handled by the top view, so the UIView will only get them if the other views don't want them. The easiest approach here is to make an invisible UIButton that covers your whole screen, and if it's tapped, dismiss the keyboard and remove the button.
Use the below approach & get that Done button on your keyboard in iOS7.
Sample code is here.
Screenshot of Done button after using this approach is here.

UIButton not registering on ios7

I have a button, it works as usual on iOS 5 and 6. But on iOS 7 when I press the button the keyboard dismisses, but the method is not called. When I press it second time it works as intended.
Why is that?
Here is the code:
[self.loginButton addTarget:self action:#selector(loginButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
I have the button in UITableView cell.
EDIT:
Here is how I dismiss keyboard at the beginning of this method, but this method is not getting called o iOS7 until the second time I tap on the button.
for (UITextField *field in #[self.loginField, self.passwordField]) {
if ([field isFirstResponder]) {
[field resignFirstResponder];
}
}
I also have gesture recognizer to remove keyboard on tap outside:
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(removeKeyboard)];
[self.view addGestureRecognizer:tapper];
tapper.cancelsTouchesInView = NO;
Here is its method:
- (void) removeKeyboard
{
[self traverseAllSubviewsOfView:self.view withBlock:^(UIView *inView) {
[inView resignFirstResponder];
}];
}
Here is what helped me - I set controller as delegate for gesture recognizer and implement following method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// test if touch is on button
if ([touch.view isKindOfClass:[UIControl class]]) {
return NO;
}
return YES; // handle the touch
}
Did you check the cancelsTouchesInView property of the gesture recognizer? This property controls if touches are delivered to any underlying views. So settings this to NO should allow the touch to be sent to your button. More detail here.
This property can be set in code or in Interface Builder. You can set it in IB by highlighting the gesture recognizer and uncheck "Cancels Touches in View" (I think) in the Attributes Inspector.
I'm not in front of a Mac currently so I can't confirm the exact wording.

how to remove multiple picker from the view.window

In my app on button click i have add pickerview to the windows below is the code show that
- (IBAction)btnMake:(id)sender
{
pickerViewMake = [[UIPickerView alloc] initWithFrame:pickerFrame];
pickerViewMake.showsSelectionIndicator = YES;
pickerViewMake.dataSource = self;
pickerViewMake.delegate = self;
[pickerViewMake setBackgroundColor:[UIColor clearColor]];
[viewPicker addSubview:pickerViewMake];
[self.view.window addSubview:pickerViewMake];
}
-(void)removeAllPicker
{
[pickerViewMake removeFromSuperview];
[pickerViewModel removeFromSuperview];
[pickerViewYear removeFromSuperview];
[pickerViewTrim removeFromSuperview];
}
remove all picker is the method used for removing the picker from superview
it works
The problem is that on multiple clicking btnMake number of the picker view cones up then
removeallPicker method call than also pickerview does not remove from superview.
Do one Thing create one BOOL variable and set in viewdidload yes and check in function if it is yes then picker come and also make it no in btnmake method.
Try in the following way
if (<instance>)
{
[<instance> removeFromSuperview],<instance> = nil;
}
After this create your picker then it won't create multiple times, it will create only once.

Resources