How to find UIButton by title - objective-c

I am having a UIView that has button in it. The buttons are not in some array.
I would like to look at the whole UIView (using for loop) and look for a button by its title, and later remove it.
- (void)removeButtonByTitle:(NSString*)name
{
for (buttons in view) {
// find the button with the name "name" and remove it from the view
}
}
I couldn't find a way to do that without saving their names/pointers to an array.

It's generally preferable to use the tag property for this. Then you can simply find the button with the viewWithTag: method and don't have to adjust your code if you decide to change the button title or localize your app.
If you really need to find a button by its title, you could do it like this:
NSString *buttonTitle = #"name";
UIButton *buttonWithTitle = nil;
for (UIButton *button in view.subviews) {
if (![button isKindOfClass:[UIButton class]]) continue;
if ([[button currentTitle] isEqualToString:buttonTitle]) {
buttonWithTitle = button;
break;
}
}
//do something with the button...

Well you could do it like this:
for (UIView *v in view.subviews)
if ([v isKindOfClass:[UIButton class]] && [[(UIButton *)v currentTitle] isEqualToString:#""])
//remove
But I must say that doesn't sound like a robust solution, your button title could change during localization or it may be different for different states.

Do that:
- (void)removeButtonByTitle:(NSString *)name
{
for (UIView *tempView in self.subviews)
{
if ([tempView isKindOfClass:[UIButton class]]) // make sure it's actually a UIButton
{
UIButton *button = tempView;
if ([button.titleLabel.text isEqualToString:name]) // compare the title
{
[button removeFromSuperview];
}
}
}
}

Check this:
-(void)removeButtonWithTitle:(NSString*)titleString {
NSArray *subViews = [self.view subviews];
[subViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if([obj isKindOfClass:[UIView class]]) {
NSArray *subViewsArray = [(UIView*)obj subviews];
for (int i=0; i<[subViewsArray count]; i++) {
id subViewObj = [subViewsArray objectAtIndex:i];
if([subViewObj isKindOfClass:[UIButton class]] && [[(UIButton*)subViewObj titleLabel].text isEqualToString:titleString]) {
[subViewObj removeFromSuperview];
*stop = YES;
break;
}
}
}
}];
}

Related

iOS 8 custom input view background color

I have a custom input view that I swap out with the iOS keyboard. Previous to iOS 8, for iOS 7 I "sussed out" the keyboard backdrop by finding the subview of class UIKBInputBackdropView (contained by a UIPeripheralHostView). Then I was able to set the alpha of the backdrop view to get a clear custom input view backdrop.
With iOS 8, this no longer works (i realize it is unsupported API and this is the risk). Through some experimentation and reading here, it seems that the custom input view is now found in a view hierarchy as such:
UIInputSetContainerView -> UIInputSetHost
It looks like there is no longer a backdrop view that is providing the opacity behind the custom input view. Can someone point me to how get rid of the translucent/blur effect behind my custom input view?
I had same issue on iOS 8 and there is way to remove background from input view.
- (void)removeKeyboardBackground
{
// Locate non-UIWindow.
UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
if (![[testWindow class] isEqual:[UIWindow class]]) {
keyboardWindow = testWindow;
break;
}
}
// Locate background.
for (UIView *possibleFormView in [keyboardWindow subviews]) {
if ([[possibleFormView description] hasPrefix:#"<UIInputSetContainerView"]) {
for (UIView* peripheralView in possibleFormView.subviews) {
if ([[peripheralView description] hasPrefix:#"<UIInputSetHostView"]) {
for (UIView* backSubiview in peripheralView.subviews) {
if ([[backSubiview description] hasPrefix:#"<UIKBInputBackdropView"]) {
[[backSubiview layer] setOpacity:0.0];
}
}
}
}
}
}
}
I used a variant of the accepted answer for later iOS versions. It looks like Apple has pushed the UIKBInputBackdropView under another UIView now:
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:(v) options:NSNumericSearch] != NSOrderedAscending)
- (void)removeKeyboardBackground
{
NSString *viewPath;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"11.0"))
viewPath = #"UIRemoteKeyboardWindow/UIInputSetContainerView/UIInputSetHostView/UIView/UIKBInputBackdropView";
else
viewPath = #"UIRemoteKeyboardWindow/UIInputSetContainerView/UIInputSetHostView/UIKBInputBackdropView";
NSArray *appWindows = [NSMutableArray arrayWithArray:[[UIApplication sharedApplication] windows]];
NSArray *viewsFound = [self viewsFromViews:appWindows AtPath:[viewPath componentsSeparatedByString:#"/"]];
for (UIView *background in viewsFound)
background.layer.opacity = 0.0;
}
- (NSArray<__kindof UIView *> *)viewsFromViews:(NSArray<__kindof UIView *> *)views AtPath:(NSArray<NSString *> *)path
{
NSMutableArray *viewsFound = [NSMutableArray array];
if (views != nil && path != nil && [views count] > 0 && [path count] > 0)
{
NSString *prefix = [#"<" stringByAppendingString:[path firstObject]];
NSArray *pathRemaining = [path count] <= 1 ? nil : [path subarrayWithRange:NSMakeRange(1, [path count] - 1)];
for (UIView *view in views)
if ([[view description] hasPrefix:prefix]) {
if (pathRemaining == nil)
[viewsFound addObject:view];
else
[viewsFound addObjectsFromArray:[self viewsFromViews:[view subviews] AtPath:pathRemaining]];
}
}
return viewsFound;
}

Objective C UIActionSheet custom button color

I'm trying to change the color of the button in my UIActionSheet. The problem (a really weird problem) is that if the orientation of the device is Landscape the button color doesn't change!
Some screenshot:
I can't figure out why!
Here's the code:
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
{
[actionSheet.subviews enumerateObjectsUsingBlock:^(id _currentView, NSUInteger idx, BOOL *stop) {
if ([_currentView isKindOfClass:[UIButton class]]) {
[((UIButton *)_currentView).titleLabel setTextColor:[[MSAppDelegate sharedInstance] defaultColor]];
}
}];
/*
for (UIView *subview in actionSheet.subviews) {
if ([subview isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)subview;
[button setTitleColor:[[MSAppDelegate sharedInstance] defaultColor] forState:UIControlStateNormal];
[button setTitleColor:[[MSAppDelegate sharedInstance] defaultColor] forState:UIControlStateSelected];
}
if ([subview isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)subview;
label.textColor = [[MSAppDelegate sharedInstance] defaultColor];
}
}
*/
}
I also tried the commented code, both work in Portrait but not in Landscape!
By the way either in Landscape and in Portrait the code is executed!
If you want to see all the code here's the link
Thank you in advance!
You could Subclass the UIActionSheet class and implement the tableView:willDisplayCell:forRowAtIndexPath: selector:
#interface MSActionSheet : UIActionSheet
#end
#implementation MSActionSheet
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[[cell subviews] enumerateObjectsUsingBlock:^(id v, NSUInteger idx, BOOL *stop) {
[[v subviews] enumerateObjectsUsingBlock:^(id v2, NSUInteger idx, BOOL *stop) {
if ([v2 isKindOfClass:[UILabel class]]) {
[(UILabel *)v2 setTextColor:[[MSAppDelegate sharedInstance] defaultColor]];
}
}];
}];
}
#end
use this method to set button title color then i am sure your problem will solve
[[[actionSheet valueForKey:#"_buttons"] objectAtIndex:0] setTitleColor:[[MSAppDelegate sharedInstance] defaultColor] forState:UIControlStateNormal];

Custom InputView for UISearchBar doesn't work after tap cancel button in iOS7

In my iOS App , i need to set custom inputView for UISearchBar in iOS7.
So i wrote following codes.
NSArray *searchBarSubViews = [[self.sBar.subviews objectAtIndex:0] subviews];
for(int i =0; i<[searchBarSubViews count]; i++) {
if([[searchBarSubViews objectAtIndex:i] isKindOfClass:[UITextField class]])
{
UITextField* search=(UITextField*)[searchBarSubViews objectAtIndex:i];
[search setFont:[UIFont fontWithName:#"CustomFont" size:15]];
search.delegate = self;
[search setInputView:self.customKeyboard];
[self.customKeyboard setTextView:search];
}
}
It is working fine. However when i type with my custom keyboard and tap Cancel Button to resignFirstResponder.
And i tap UISearchBar again, i can't type any text in UISearchBar , including native english keyboard.
And also Cancel Button is hiding and UISearchBar is not working anymore.
I don't know why is happening?
How can i solve it?
I tried your code and it's working fine with my app. In fact thanks and congratulations seem to be in order, because for one reason or other you seem to have solved a problem others on this site had to built more elaborate workarounds for.
In my app the keyboard changes according to the selected scope of the searchbar:
scope == 0 presents my CustomKeyboard
scope != 0 presents the usual iPhone Keyboard.
My implementation of your code looks like this:
-(UITextField *)textFieldFormSearchBar{
NSArray *searchBarSubViews = [[self.searchBar.subviews objectAtIndex:0] subviews];
for(int i =0; i<[searchBarSubViews count]; i++) {
if([[searchBarSubViews objectAtIndex:i] isKindOfClass:[UITextField class]])
{
UITextField* search=(UITextField*)[searchBarSubViews objectAtIndex:i];
return search;
}
}
return nil;
}
-(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
if (searchBar.selectedScopeButtonIndex==0) {
self.textFieldFormSearchBar.delegate = self;
self.textFieldFormSearchBar.inputView=self.customKeyboard;
self.customKeyboard.field=self.textFieldFormSearchBar;
}
else{
if (self.customKeyboard.superview!=nil) [self.customKeyboard removeFromSuperview];
self.textFieldFormSearchBar.delegate = self;
self.textFieldFormSearchBar.inputView=nil;
self.customKeyboard.field=nil;
}
return YES;
}
-(void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope{
if (selectedScope==0) {
self.textFieldFormSearchBar.delegate = self;
self.textFieldFormSearchBar.inputView=self.customKeyboard;
self.customKeyboard.field=self.textFieldFormSearchBar;
}
else{
if (self.customKeyboard.superview!=nil) [self.customKeyboard removeFromSuperview];
self.textFieldFormSearchBar.delegate = self;
self.textFieldFormSearchBar.inputView=nil;
self.customKeyboard.field=nil;
}
[self.textFieldFormSearchBar reloadInputViews];
}
-(BOOL)searchBarShouldEndEditing:(HDLSearchBar *)searchBar{
if (self.customKeyboard) {
[self.customKeyboard removeFromSuperview];
}
return [self.textFieldFormSearchBar resignFirstResponder];
}
Probably not the most efficient way to do it, but it does the trick. I hope you'll find something that helps you solve your problem.

NSArray remove last object of type?

Working with an array of UIViews and UIImageViews ([[[UIApplication sharedApplication] window] subviews]). I need to remove only the object of the highest index of the UIImageView type.
You can use indexOfObjectWithOptions:passingTest: method to search the array in reverse for an object that passes a test using a block, and then delete the object at the resulting position:
NSUInteger pos = [myArray indexOfObjectWithOptions:NSEnumerationReverse
passingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [obj isKindOfClass:[UIImageView class]]; // <<== EDIT (Thanks, Nick Lockwood!)
}];
if (pos != NSNotFound) {
[myArray removeObjectAtIndex:pos];
}
another block-based solution
[window.subviews enumerateObjectsWithOptions:NSEnumerationReverse
usingBlock:^(id view, NSUInteger idx, BOOL *stop)
{
if ([view isKindOfClass:[UIImageView class]]){
[view removeFromSuperview];
*stop=YES;
}
}];
non-block solution:
for (UIView *view in [window.subview reverseObjectEnumerator])
{
if ([view isKindOfClass:[UIImageView class]]){
[view removeFromSuperview];
break;
}
}
I published some demo code, that shows both solutions.
How about:
UIWindow *window = [[UIApplication sharedApplication] window];
UIView *imageView = nil;
for (UIView *view in window.subviews)
{
if ([view isKindOfClass:[UIImageView class]])
{
imageView = view;
}
}
//this will be the last imageView we found
[imageView removeFromSuperview];

Resign first responder from separate class

I have a class that makes a keyboard toolbar which has "Next", "Previous", and "Done" buttons on it. Is there a way for this class to know (or find out) what objects are on the screen at any time?
E.g., can it see what the current view is and what the text fields on it are, and then be able to resign the first responder?
If you specifically want to resign first responder without the need to known which view is the first responder you can send resignFirstResponder to "nil" like this:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
This is documented behaviour although I cannot find in the docs right now.
Is there a way for this class to know
(or find out) what objects are on the
screen at the time?
Find the momma view and you can iterate through all the objects on the screen (because they will be UIViews too) like this. Note that you may need to add recursion:
for (UIView *view in mommaView.subviews) {
do something to the view
}
You can start at the Window class and go down from there, asking [view respondsTo:#selector(isFirstResponder) && [view isFirstResponder] on each. Some debugging code that I use might come in handy as a template and also while debugging:
+ (void) dumpWindowFrom:(NSString *) fromText {
[self dumpViews:[[UIApplication sharedApplication] keyWindow] from:fromText];
}
void dumpViewsRecursive(UIView* view, NSString *text, NSString *indent)
{
Class cl = [view class];
NSString *classDescription = [cl description];
// while ([cl superclass]) //restore to print superclass list
// {
// cl = [cl superclass];
// classDescription = [classDescription stringByAppendingFormat:#":%#", [cl description]];
// }
if ([text compare:#""] == NSOrderedSame)
NSLog(#"%d: %# %# %#", (int)view, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
else
NSLog(#"%d: %# %# %# %#", (int)view, text, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
for (NSUInteger i = 0; i < [view.subviews count]; i++)
{
UIView *subView = [view.subviews objectAtIndex:i];
NSString *newIndent = [[NSString alloc] initWithFormat:#" %#", indent];
NSString *msg = [[NSString alloc] initWithFormat:#"%#%d:", newIndent, i];
dumpViewsRecursive (subView, msg, newIndent);
[msg release];
[newIndent release];
}
}
+ (void) dumpViews: (UIView *) view {
dumpViewsRecursive (( (!view) ? [[UIApplication sharedApplication] keyWindow] : view), #"" ,#"");
}
+ (void) dumpViews: (UIView *) view from:(NSString *) fromText{
dumpViewsRecursive ((!view) ? [[UIApplication sharedApplication] keyWindow] : view, fromText, #"");
}
yes, the methods provided below will be called whenever a textField becomes Active. I think you are looking for
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return 1;
}
or
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
- (void) textFieldDidEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
and if you are looking for a specific textField in your view, you should assign them tags:
textField.tag =1 // for textField 1
textField.tag =2 // for textField 2
// You may check for these tags and then resign specific ones.