GMSMapView custom my location button image - ios

I would like to know if it is possible to change the my location button image from GMSMapView to a custom image.

1) Know frame location button:
for (UIView *object in self.mapView.subviews) {
if([[[object class] description] isEqualToString:#"GMSUISettingsView"] )
{
for(UIView *view in object.subviews) {
if([view.accessibilityIdentifier isEqual:#"my_location"] ) {
frame = view.frame;
}
}
}
}
Use debug to see correct frame!!!
2) Don't use self.mapView.settings.myLocationButton = YES;
3) Create own button to this frame:
UIButton *buttonLocation = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonLocation.frame = CGRectMake([UIScreen mainScreen].bounds.size.width - 66, [UIScreen mainScreen].bounds.size.height - frame.size.height, frame.size.width, frame.size.height);
[buttonLocation addTarget:self
action:#selector(bLocations:)
forControlEvents:UIControlEventTouchUpInside];
[buttonLocation setImage:[[UIImage imageNamed:#"image.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal];
[self.mapView addSubview:buttonLocation];

Following the previous answers (using the method of getting the UIButton reference from the GMSMapView), here is an updated answer in Swift. It replaces the default My Location icon with your own custom image.
private func setupMyLocationButton() {
let locationButton = mapView.subviews
.filter { $0.description.contains("GMSUISettingsPaddingView") }
.flatMap { $0.subviews }
.flatMap { $0.subviews }
.filter { $0.description.contains("GMSx_QTMButton") }
.first
let customImage = UIImage(imageLiteralResourceName: "yourCustomIconFilename")
let myLocationButton = locationButton as? UIButton
myLocationButton?.setImage(customImage, for: .normal)
}

In order to do that, you can retrieve the UIButton reference to that button from the GMSMapView. After that you just use the standard [UIButton setImage:] for all the different states. I created a category for getting this UIButton from the GMSMapView control:
https://github.com/trant/GMSMapView-Additions

Related

iOS number pad keyboard with ability to switch on text keyboard

I have a text field that I want to have a keyboard like this when user start typing:
please also see this video: https://youtu.be/iU_jocny3N0
As you can see in this video there is a "ABC" key that helps user to switch from number pad to text. and also when press "123" in text the keyboard switchs from text to number pad. I am wondering how they do this?
The only solution that I found was adding a subview to keyboard like what described here:
Adding Done Button to Only Number Pad Keyboard on iPhone
but this way may not work when user uses custom keyboards. and also do not works for switching from text to number pad.
Or as another solution I know accessoryInputView but this is not like the video. It adds a toolbar above the keyboard.
Does someone knows the solutions that is used in this video?
I have added comma button to the keyboard,
Keyboard is also a simple UIView Which contains Controls
NOTE: This is old code was working in my old project Not tested in new projects
- (void) keyboardWillShow:(NSNotification *)note {
// create custom button
dispatch_async(dispatch_get_main_queue(), ^{
// Some code
UITextField *txt = (UITextField *)[self.view findFirstResponder];
if (txt.keyboardType == UIKeyboardTypeDecimalPad) {
UIButton * btnComma = [UIButton buttonWithType:UIButtonTypeCustom];
[btnComma setTag:15000];
UIView* keyboard = [self findKeyboard];
// btnComma.frame = CGRectMake(0, 162, 126, 54);
btnComma.frame = [self findKeySizeForView:keyboard];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[btnComma setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, 20, 0)];
}
[btnComma setBackgroundColor:[UIColor colorWithHexString:#"CBD0D6"]];
btnComma.adjustsImageWhenHighlighted = NO;
[btnComma setTitle:#"." forState:UIControlStateNormal];
[btnComma setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btnComma.titleLabel setFont:[UIFont systemFontOfSize:35.0f]];
[btnComma addTarget:self action:#selector(commaBtnTapped) forControlEvents:UIControlEventTouchUpInside];
[keyboard addSubview:btnComma];
btnComma = nil;
}
});
}
- (UIView *) viewWithPrefix:(NSString *)prefix inView:(UIView *)view {
for (UIView *subview in view.subviews) {
if ([[subview description] hasPrefix:prefix]) {
return subview;
}
}
return nil;
}
This method for finding keyboard from UIWindow
- (UIView *) findKeyboard {
for (UIWindow* window in [UIApplication sharedApplication].windows) {
UIView *inputSetContainer = [self viewWithPrefix:#"<UIInputSetContainerView" inView:window];
if (inputSetContainer) {
UIView *inputSetHost = [self viewWithPrefix:#"<UIInputSetHostView" inView:inputSetContainer];
if (inputSetHost) {
UIView *kbinputbackdrop = [self viewWithPrefix:#"<_UIKBCompatInput" inView:inputSetHost];
if (kbinputbackdrop) {
UIView *theKeyboard = [self viewWithPrefix:#"<UIKeyboard" inView:kbinputbackdrop];
return theKeyboard;
}
}
}
}
return nil;
}
and For finding size of bottom right button
- (CGRect ) findKeySizeForView:(UIView *)view {
if (view != nil) {
UIView *uiKeyboardImpl = [self viewWithPrefix:#"<UIKeyboardImpl" inView:view];
if (uiKeyboardImpl != nil) {
UIView *uiKeyboardLayoutStar = [self viewWithPrefix:#"<UIKeyboardLayoutStar" inView:uiKeyboardImpl];
if (uiKeyboardLayoutStar != nil) {
UIView *uiKBKeyplaneView = [self viewWithPrefix:#"<UIKBKeyplaneView" inView:uiKeyboardLayoutStar];
if (uiKBKeyplaneView != nil) {
for (view in [uiKBKeyplaneView subviews]) {
CGPoint pointOrigin = view.layer.frame.origin;
if (pointOrigin.x <= 0 && pointOrigin.y == uiKBKeyplaneView.frame.size.height - view.frame.size.height && [[view description] hasPrefix:#"<UIKBKeyView"])
return view.layer.frame;
}
}
}
}
}
return CGRectZero;
}

UIButton suddenly stops working

So I'm having issues with the buttons for the filters in my photo app. I'm not exactly sure why, but they've suddenly stopped working. The selector is not being called when they're tapped, but I have no idea why. They were working just fine a few days ago, but for some reason they're not now. I've checked the UIView subviews array, verified that it's right on top. I need a way to see if, for some reason, the touches are not making it to the button. I'm not sure how to do this.
I wish I had more information to give, but this is all I've got. I hope someone has some suggestions because I'm at wit's end with it.
Thanks in advance!
Button Creation Method:
-(void)createFilterButtonsForThumbnail:(UIImage *)thumbnail
{
UIView *filtersContainer = self.filterContainer;
CGRect buttonFrame = CGRectMake(kFilterFrameThickness, kFilterFrameThickness,
thumbnail.size.width, thumbnail.size.height);
CGFloat frameWidth = thumbnail.size.width+(2*kFilterFrameThickness);
CGFloat frameHeight = kFilterPickerHeight;
UIEdgeInsets backgroundInsets = UIEdgeInsetsMake(0, kFilterFrameThickness, 0, kFilterFrameThickness);
UIImage *buttonBackgroundImage = [[UIImage imageNamed:#"FilmReel"] resizableImageWithCapInsets:backgroundInsets];
for (int i = 0;i<(self.filterPaths.count+kFilterNonLookups+1);i++){
UIImageView *buttonBackground = [[UIImageView alloc] initWithFrame:CGRectMake(kFilterSidePadding+(i*(frameWidth+kFilterSidePadding)),
0,
frameWidth,
frameHeight)];
[buttonBackground setImage:buttonBackgroundImage];
UIButton *thumbnailButton = [[UIButton alloc] initWithFrame:buttonFrame];
UIImage *filteredThumbnail = [self applyFilterAtIndex:i ToImage:thumbnail];
[thumbnailButton setImage:filteredThumbnail forState:UIControlStateNormal];
[thumbnailButton addTarget:self action:#selector(filterSelected:) forControlEvents:UIControlEventTouchUpInside];
[thumbnailButton setTag:i];
[buttonBackground addSubview:thumbnailButton];
[filtersContainer addSubview:buttonBackground];
if ((i > (kFilterProMinimumIndex)) && ([self isProVersion]) == NO){
UIImageView *proTag = [[UIImageView alloc] initWithImage:nil];
CGRect proFrame = CGRectMake(buttonFrame.origin.x,
buttonFrame.origin.y + buttonFrame.size.height - kFilterProIconHeight-kFilterFrameThickness,
kFilterProIconWidth,
kFilterProIconHeight);
[proTag setBackgroundColor:[UIColor orangeColor]];
[proTag setFrame:proFrame];
[thumbnailButton addSubview:proTag];
[self.filterProTags addObject:proTag];
}
}
}
Selector Method:
-(void)filterSelected:(UIButton *)button
{
NSLog(#"Pressed button for index %i",button.tag);
int buttonTag = button.tag;
if ((buttonTag < kFilterProMinimumIndex+1) || ([self isProVersion] == YES)){
[self.imageView setImage:[self applyFilterAtIndex:buttonTag ToImage:self.workingImage]];
}
else {
[self processProPurchaseAfterAlert];
}
}
I see a couple issues in this, but I'm not sure which ones are causing it to break. First, you should instantiate your button using buttonWithType: on UIButton:
UIButton *thumbnailButton = [UIButton buttonWithType:UIButtonTypeCustom];
[thumbnailButton setFrame:buttonFrame]
The reason for this is UIButton is a class cluster, and you should let the OS give you the correct button.
Secondly, you're adding the Button as a Subview of an UIImageView, which by default, has userInteractionEnabled set to NO.
This property is inherited from the UIView parent class. This class
changes the default value of this property to NO.
You should have the button be one large button, with the background of the button be the image you want it to be.

How to determine position of UIBarButtonItem in UIToolbar?

What's the easiest way to determine the x,y location of a UIBarButtonItem in a UIToolbar?
The only answer I found is in any way to know where uibarbuttonitem has been drawn.
All proposed answers seem too complicated. There ought to be a simpler way to get the position of the damn UIBarButtonItem isn't there?
I used this which seems to be the most elegant way
- (CGRect)frameForBarButtonItem:(UIBarButtonItem *)buttonItem
{
UIView *view = [buttonItem valueForKey:#"view"];
return view ? view.frame : CGRectZero;
}
Unfortunately, there is no easy way of determining the position of a UIBarButtonItem. A UIBarButtonItem is essentially a NSObject that does just two things: describe the look and feel of a toolbar button, and forward events to the designated target/action selector.
Now, given that all buttons are subviews of UIToolbar, and all button events are routed through their respective UIBarButtonItems, it's quite trivial to loop through all subviews of your UIToolbar and when you find a button whose target is that of your UIBarButtonItem, just get the frame of that button. Some code:
UIToolbar *toolbar = <your toolbar>;
UIBarButtonItem *barButtonItem = <your item>;
UIButton *button = nil;
for (UIView *subview in toolbar.subviews) {
if ([subview isKindOfClass:[UIButton class]]) {
for (id target in [(UIButton *)subview allTargets]) {
if (target == barButtonItem) {
button = (UIButton *)subview;
break;
}
}
if (button != nil) break;
}
}
CGRect frame = button.frame;
This works for me when using popovers from UIBarButtonItems.
- (void)buttonClicked:(id)sender{
{
CGRect buttonRect = [[sender view] frame];
}
For iOS 11 (and maybe above) the call:
if let view = barButtonItemView.value(forKey: "view") as? UIView {
let viewFrame = view.frame
}
will return a zero point origin for the view. To counter this, ask for the window coordinates of the view, by using:
let actualPointOnWindow = view.convert(view.frame.origin, to: nil)
If your toolbar has any translations subtract them from the calculated point to find its position on the toolbar.
Complete Code:
if let view = barButtonItemView.value(forKey: "view") as? UIView {
let viewFrame = view.frame
let actualPointOnWindow = view.convert(view.frame.origin, to: nil)
}
A more 'swifty' SWIFT way:
func userAction(sender: AnyObject) {
if let originView = sender.valueForKey("view") {
let frame = originView.frame //it's a UIBarButtonItem
} else {
let frame = sender.frame //it's a regular UIButton
}
}
UIBarButtonItem is NOT a subclass of UIView even though it's essentially a button. Go figure.
I used johosher's approach to get the position of a UIBarButtonItem using Swift.
Since I needed the position as sourceRect for a popoverPresentationController I had to convert it to self.view. I am not sure, whether this is a good solution but it works very well and the popover shows off right of the UIBarButtonItem.
let senderRect = sender.view!!.convertRect(sender.view!!.bounds, toView: self.view)
// additional code for the popover controller
alertController.modalPresentationStyle = UIModalPresentationStyle.Popover
alertController.popoverPresentationController?.sourceRect = senderRect
alertController.popoverPresentationController?.sourceView = self.view
UIToolbar *toolbar;
for (UIView *v in [toolbar items])
{
if ([v isKindOfClass:[UIBarButtonItem class]])
{
UIBarButtonItem *b = (UIBarButtonItem*)v;
//do something with b..
}
}

Styling the cancel button in a UISearchBar

I have a UISearchBar that has a cancel button (it's displayed using -(void)setShowsCancelButton:animated). I've changed the tintColor of the search bar like this in an attempt to get a grayish searchbar:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
searchBar.tintColor = [UIColor colorWithWhite:0.8 alpha:1.0];
This is what it looks like now - notice how the cancel button is also gray: http://twitpic.com/c0hte
Is there a way to set the color of the cancel button separately so it looks more like this: http://twitpic.com/c0i6q
You can use UIAppearance to style the cancel button without iterating subviews of the UISearchBar, but the UIButton header does not currently have any methods annotated with UI_APPEARANCE_SELECTOR.
EDIT: Drill down the subviews till you get that cancel button
But this usually returns nil until
searchBar.setShowsCancelButton(true, animated: true) is called.
extension UISearchBar {
var cancelButton : UIButton? {
if let view = self.subviews.first {
for subView in view.subviews {
if let cancelButton = subView as? UIButton {
return cancelButton
}
}
}
return nil
}
}
In iOS 5.0+, you can use the appearnce proxy.
Before the search bar is showed.:
UIBarButtonItem *searchBarButton = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
[searchBarButton setBackgroundImage:myCancelButtonImageNormal forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[searchBarButton setBackgroundImage:myCancelButtonImageHighlighted forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesNormal forState:UIControlStateNormal];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesHighlighted forState:UIControlStateHighlighted];
If you use [UIButton appearanceWhenContainedIn:[UISearchBar class], nil], it will affect other buttons (e.g. clear button). So, you'd better not use UIButton's appearnce. Try UIBarButtonItem.
Change the title of 'Cancel' button:
[[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:#"newTitle" forState:UIControlStateNormal];
Swift equivalent:
let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
cancelButton?.setTitle("cancel".localized, for: .normal)
Though this might not be exactly relevant to the original question, the solution is still applicable in the larger sense of trying to customize the Cancel button in the UISearchBar. Thought this will help others who are stuck in such a scenario.
My situation was to change the cancel button's title, but with a twist, wherein I did not want to show the cancel button by default but only wanted it to show up, when the user enters the search mode (by clicking inside the search text field). At this instant, I wanted the cancel button to carry the caption "Done" ("Cancel" was giving a different meaning to my screen, hence the customization).
Nevertheless, here's what I did (a combination of caelavel's and Arenim's solutions):
Subclassed UISearchBar as MyUISearchBar with these two methods:
-(void) setCloseButtonTitle: (NSString *) title forState: (UIControlState)state
{
[self setTitle: title forState: state forView:self];
}
-(void) setTitle: (NSString *) title forState: (UIControlState)state forView: (UIView *)view
{
UIButton *cancelButton = nil;
for(UIView *subView in view.subviews){
if([subView isKindOfClass:UIButton.class])
{
cancelButton = (UIButton*)subView;
}
else
{
[self setTitle:title forState:state forView:subView];
}
}
if (cancelButton)
[cancelButton setTitle:title forState:state];
}
And in the viewcontroller which uses this Searchbar, the following piece of code takes care of showing the cancel button and customizing its title:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
MyUISearchBar *sBar = (MyUISearchBar *)searchBar;
[sBar setShowsCancelButton:YES];
[sBar setCloseButtonTitle:#"Done" forState:UIControlStateNormal];
}
Strangely enough, I did not have to do anything to hide the cancel button, as it is hidden by default, when the search mode is exited.
What you want to do is pretty tough. There is no built-in hook to get at the cancel button.
However, there are a couple of options if you are willing to jimmy open the hood.
First off, UISearchBar is a UIView, and the Cancel button is also a view, which is added into the search bar as a subview, just as you would expect.
I have experimented a little, and can tell you that when the button is onscreen it has a size of 48,30.
So in viewWillAppear, you can do something like this:
Find the cancel button view in [searchBar subviews] by looking for one with size 48,30. (There only seems to be one -- this could change...) You could be doubly careful and look for one that is in approximately the correct position (differs in landscape and portrait).
Add a subview to the cancel button.
The subview should be a UIControl (so that you can set enabled = NO, in order to make sure touch events get to the actual cancel button)
It needs to have the right color and rounded corners; you will need to fudge the size for reasons I don't yet understand (55,30 seems to work)
This will work if searchBar.showsCancelButton is always YES; if you want it to disappear when not editing the search string, you will need to find a hook to add the overlay each time the cancel button appears.
As you can see, this is some ugly tinkering. Do it with eyes wide open.
You can find the cancel button by looping through the subviews of the search bar and checking for the class type (instead of the size):
UIButton *cancelButton = nil;
for(UIView *subView in yourSearchBar.subviews){
if([subView isKindOfClass:UIButton.class]){
cancelButton = (UIButton*)subView;
}
}
And then change the tint color:
[cancelButton setTintColor:[UIColor colorWithRed:145.0/255.0 green:159.0/255.0 blue:179.0/255.0 alpha:1.0]];
If you want to configure your cancel button on UISearchBar you should get the UIButton object from your UISearchBar object. Example below
UISearchBar *s_bar = [[UISearchBar alloc] initWithFrame:CGRectMake(50,20,300,30)];
s_bar.delegate = self;
s_bar.barStyle = UIBarStyleDefault;
s_bar.showsCancelButton = YES;
UIButton *cancelButton;
for (id button in s_bar.subviews)
{
if ([button isKindOfClass:[UIButton class]])
{
cancelButton=(UIButton*)button;
break;
}
}
Custom UISearchBar and override method -addSubview:
- (void) addSubview:(UIView *)view {
[super addSubview:view];
if ([view isKindOfClass:UIButton.class]) {
UIButton *cancelButton = (UIButton *)view;
[cancelButton setBackgroundImage:[UIImage imageNamed:#"xxxx.png"] forState:UIControlStateNormal];
[cancelButton setBackgroundImage:[UIImage imageNamed:#"yyyy.png"] forState:UIControlStateHighlighted];
}
}
I'll give a detailed answered regarding the UIAppearance technique. First, you need to understand that the cancel button is a private UINavigationButton:UIButton. After some inspection, it appears that UINavigationButton will respond to those UIAppearance selectors:
// inherited from UINavigationButton
#selector(setTintColor:)
#selector(setBackgroundImage:forState:style:barMetrics:)
#selector(setBackgroundImage:forState:barMetrics:)
#selector(setTitleTextAttributes:forState:)
#selector(setBackgroundVerticalPositionAdjustment:forBarMetrics:)
#selector(setTitlePositionAdjustment:forBarMetrics:)
#selector(setBackButtonBackgroundImage:forState:barMetrics:)
#selector(setBackButtonTitlePositionAdjustment:forBarMetrics:)
#selector(setBackButtonBackgroundVerticalPositionAdjustment:forBarMetrics:)
// inherited from UIButton
#selector(setTitle:forState:)
Coincidentally, those selectors match those of a UIBarButtonItem. Meaning the trick is to use two separate UIAppearance to handle the private class UINavigationButton.
/* dual appearance technique by Cœur to customize a UINavigationButton */
Class barClass = [UISearchBar self];
UIBarButtonItem<UIAppearance> *barButtonItemAppearanceInBar = [UIBarButtonItem appearanceWhenContainedIn:barClass, nil];
[barButtonItemAppearanceInBar setTintColor:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... style:... barMetrics:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... barMetrics:...];
[barButtonItemAppearanceInBar setTitleTextAttributes:... forState:...];
[barButtonItemAppearanceInBar setBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
[barButtonItemAppearanceInBar setTitlePositionAdjustment:... forBarMetrics:...];
// only for a backButton in an UINavigationBar, not for a cancelButton in an UISearchBar
//[barButtonItemAppearanceInBar setBackButtonBackgroundImage:... forState:... barMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonTitlePositionAdjustment:... forBarMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
UIButton<UIAppearance> *buttonAppearanceInBar = [UIButton appearanceWhenContainedIn:barClass, nil];
// warning: doesn't work for iOS7+
[buttonAppearanceInBar setTitle:... forState:...];
This will let you customize your Cancel button as much as you want.
After you've initialized your UISearchBar, you can probe into it's subviews and customize each of them. Example:
for (UIView *view in searchBar.subviews) {
//if subview is the button
if ([[view.class description] isEqualToString:#"UINavigationButton"]) {
//change the button images and text for different states
[((UIButton *)view) setEnabled:YES];
[((UIButton *)view) setTitle:nil forState:UIControlStateNormal];
[((UIButton *)view) setImage:[UIImage imageNamed:#"button image"] forState:UIControlStateNormal];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:#"button_pressed"] forState:UIControlStateSelected];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:#"button_pressed"] forState:UIControlStateHighlighted];
//if the subview is the background
}else if([[view.class description] isEqualToString:#"UISearchBarBackground"]) {
//put a custom gradient overtop the background
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[some uicolor] CGColor], (id)[[another uicolor] CGColor], nil];
[view.layer insertSublayer:gradient atIndex:0];
//if the subview is the textfield
}else if([[view.class description] isEqualToString:#"UISearchBarTextField"]){
//change the text field if you wish
}
}
Worked out great for me! Especially the gradient :)
Swift 2.1.1:
There's no simple way to hook in and style the search bar, you need to grab the subview manually from the search bar and then apply your changes.
var cancelButton: UIButton
let topView: UIView = self.customSearchController.customSearchBar.subviews[0] as UIView
for subView in topView.subviews {
if subView.isKindOfClass(NSClassFromString("UINavigationButton")!) {
cancelButton = subView as! UIButton
cancelButton.enabled = true
cancelButton.setTitle("TestTitle", forState: UIControlState.Normal) // Change to set the title
cancelButton.setBackgroundImage(UIImage(named: "ImageName"), forState: .Normal) // Change this to set a custom cancel button image, set the title to "" to remove 'Cancel' text
}
}
First of all I'd like to thank #Eliott from this https://stackoverflow.com/a/37381821/1473144
I had to make a few adjustments for his answer to work in my specs that go below.
Please, I ask the OP to update the accepted answer as it's VERY outdated.
Swift 3, iOS 10 & Xcode 8.2.1
searchBar.showsCancelButton = true
var cancelButton: UIButton
let topView: UIView = self.searchBar.subviews[0] as UIView
for subView in topView.subviews {
if let pvtClass = NSClassFromString("UINavigationButton") {
if subView.isKind(of: pvtClass) {
cancelButton = subView as! UIButton
cancelButton.setTitle("", for: .normal)
cancelButton.tintColor = UIColor.black
cancelButton.setImage(#imageLiteral(resourceName: "searchX"), for: .normal)
}
}
}
Well, here is function, which can change Cancel's button label. Modify it, if you want.
Usage is:
nStaticReplaceStringInView(mySearchBar, #"Cancel", #"NewCancelButtonLabel");
void nStaticReplaceStringInView(UIView * view, NSString * haystack, NSString * needle)
{
for(int i=0; i<[view.subviews count]; i++)
{
nStaticReplaceStringInView([view.subviews objectAtIndex:i], haystack,needle);
}
if([view respondsToSelector:#selector(titleForState:)])
{
//NSLog(#"%# || %#",[view titleForState:UIControlStateNormal], haystack);
if(NSStrEq([view titleForState:UIControlStateNormal] , haystack))
{
[view setTitle: needle forState: UIControlStateNormal];
}
}
}
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar
{
NSArray *arr = [theSearchBar subviews];
UIButton *cancelButton = [arr objectAtIndex:3];
[cancelButton setTitle:#"yourtitle" forState:UIControlStateNormal];
}
Just take a log of arr amd see at which index control lies. In the same way u can set UITextField properties:
NSArray *arr = [searchbar subviews];
UITextField *searchfield = [arr objectAtIndex:2];
[searchfield setTextAlignment:UITextAlignmentRight];
I have many UISearchBar items throughout my app, so I wrote this category to add a property so you can access mySearchBar.cancelButton. (If you're new to categories, read more about extending objects with Categories here.)
Keep in mind you should only access this when the Cancel button is visible because UISearchBar seems to create a new button object every time it shows. Don't save the pointer to the cancelButton, just get it when needed:
#interface UISearchBar (cancelButton)
#property (readonly) UIButton* cancelButton;
- (UIButton *) cancelButton;
#end
#implementation UISearchBar (cancelButton)
- (UIButton *) cancelButton {
for (UIView *subView in self.subviews) {
//Find the button
if([subView isKindOfClass:[UIButton class]])
{
return (UIButton *)subView;
}
}
NSLog(#"Error: no cancel button found on %#", self);
return nil;
}
#end
stupid way
for(id cc in [SearchBar subviews])
{
if([cc isKindOfClass:[UIButton class]])
{
UIButton *btn = (UIButton *)cc;
......
Do whatever you want
.......
}
}
extension UISearchBar {
var cancelButton : UIButton? {
let topView: UIView = self.subviews[0] as UIView
if let pvtClass = NSClassFromString("UINavigationButton") {
for v in topView.subviews {
if v.isKind(of: pvtClass) {
return v as? UIButton
}
}
}
return nil
}
}
UISearchBar *searchBar;
[searchBar setShowsCancelButton:YES animated:YES];
UIButton *cancelButton =
YES == [searchBar respondsToSelector:NSSelectorFromString(#"cancelButton")] ?
[searchBar valueForKeyPath:#"_cancelButton"] : nil;
cancelButton.titleEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 10);
[cancelButton setTitle:#"New :)" forState:UIControlStateNormal];
For iOS 11 and Swift 4.
Create a subclass of UISearchController.
Override method:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("layout")
if let btn = searchBar.subviews[0].subviews[2] as? UIButton {
btn.frame = CGRect(x: 306, y: 20, width: 53, height: 30)
}
}
For iOS 10 & above, use following method
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:#[[UISearchBar class]]] setTintColor:[UIColor blackColor]];
The best way to style the cancelButton is without using UIAppearance is like this it is for Swift5 iOS13 and it works best with UISearchResultController.searchBar too
extension UISearchBar {
func changeSearchBarAppearance(appearance: MyAppearance) {
self.barTintColor = appearance.searchbar.barTintColor
self.tintColor = appearance.searchbar.tintColor
if let textField = self.subviews.first?.subviews.last?.subviews.first {
textField.tintColor = .black
}
}
}
setting serachBar tintColor will set the tintColor of all items including the cancelButton but with this the blinker in the searchField will also be set with the same tintColor so find the textfield and set its tintColor will solve the blinker issue

Checkbox in iOS application

I need to add checkbox controls to my form. I know that there is no such control in iOS SDK. How could I do this?
this has been driving me mad too and I found a different solution that works well for me and avoids having to use images.
Add a new label object to Interface Builder.
Create an IBOutlet property in Xcode and connect it up to it. In the code below I've called it 'fullyPaid' as I want to know if someone has fully paid a sum of money.
Add the 2 functions below.
The 'touchesBegan' function checks if you touched somewhere inside the 'fullyPaid' label object and if so, it calls the 'togglePaidStatus' function.
The 'togglePaidStatus' function sets up two strings which have the unicode characters representing an empty box (\u2610) and a checked box (\u2611) respectively. Then it compares what's currently in the 'fullyPaid' object and toggles it with the other string.
You might want to call the togglePaidStatus function in the viewDidLoad function to set it to an empty string initially.
Obviously you can add extra checks to prevent users toggling the checkbox if the label is not enabled, but that's not shown below.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if (CGRectContainsPoint([fullyPaid frame], [touch locationInView:self.view]))
{
[self togglePaidStatus];
}
}
-(void) togglePaidStatus
{
NSString *untickedBoxStr = [[NSString alloc] initWithString:#"\u2610"];
NSString *tickedBoxStr = [[NSString alloc] initWithString:#"\u2611"];
if ([fullyPaid.text isEqualToString:tickedBoxStr])
{
fullyPaid.text = untickedBoxStr;
}
else
{
fullyPaid.text = tickedBoxStr;
}
[tickedBoxStr release];
[untickedBoxStr release];
}
Generally, you would use the UISwitch for checkbox-like functionality.
You could roll your own though by using an image control with two images (checked/unchecked) and switching the images when they touch the control/
If you're showing a group of options and the user can select one of them, use a tableview with a checkmark accessory and a different text color on the selected row.
If you have a single option, your best bet is to use a switch. If you can't or don't want to, use a button, setting the normal image to an empty box and the selected image to a checked box. You'll have to make those two images yourself or find stock graphics to use for them.
Extending to Adrean's idea, i've achieved this using a very simple approach.
My idea is to change button (lets say checkBtn) text depending upon its state, and then change button's state in its IBAction.
Below is the code how i did this:
- (void)viewDidLoad
{
[super viewDidLoad];
[checkBtn setTitle:#"\u2610" forState:UIControlStateNormal]; // uncheck the button in normal state
[checkBtn setTitle:#"\u2611" forState:UIControlStateSelected]; // check the button in selected state
}
- (IBAction)checkButtonTapped:(UIButton*)sender {
sender.selected = !sender.selected; // toggle button's selected state
if (sender.state == UIControlStateSelected) {
// do something when button is checked
} else {
// do something when button is unchecked
}
}
I wanted to do this programmatically, and also solve the problem that the hit area was really too small. This is adapted from various sources, including Mike and Mike's commenter Agha.
In your header
#interface YourViewController : UIViewController {
BOOL checkboxSelected;
UIButton *checkboxButton;
}
#property BOOL checkboxSelected;;
#property (nonatomic, retain) UIButton *checkboxButton;
-(void)toggleButton:(id)sender;
And in your implementation
// put this in your viewDidLoad method. if you put it somewhere else, you'll probably have to change the self.view to something else
// create the checkbox. the width and height are larger than actual image, because we are creating the hit area which also covers the label
UIButton* checkBox = [[UIButton alloc] initWithFrame:CGRectMake(100, 60,120, 44)];
[checkBox setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
// uncomment below to see the hit area
// [checkBox setBackgroundColor:[UIColor redColor]];
[checkBox addTarget:self action:#selector(toggleButton:) forControlEvents: UIControlEventTouchUpInside];
// make the button's image flush left, and then push the image 20px left
[checkBox setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[checkBox setImageEdgeInsets:UIEdgeInsetsMake(0.0, 20.0, 0.0, 0.0)];
[self.view addSubview:checkBox];
// add checkbox text text
UILabel *checkBoxLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 74,200, 16)];
[checkBoxLabel setFont:[UIFont boldSystemFontOfSize:14]];
[checkBoxLabel setTextColor:[UIColor whiteColor]];
[checkBoxLabel setBackgroundColor:[UIColor clearColor]];
[checkBoxLabel setText:#"Checkbox"];
[self.view addSubview:checkBox];
// release the buttons
[checkBox release];
[checkBoxLabel release];
And put this method in too:
- (void)toggleButton: (id) sender
{
checkboxSelected = !checkboxSelected;
UIButton* check = (UIButton*) sender;
if (checkboxSelected == NO)
[check setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
else
[check setImage:[UIImage imageNamed:#"checkbox-checked.png"] forState:UIControlStateNormal];
}
Here is my version of checkbox for iphone.
It is single class which extends UIButton.
It is simple so i will paste it here.
CheckBoxButton.h file content
#import <UIKit/UIKit.h>
#interface CheckBoxButton : UIButton
#property(nonatomic,assign)IBInspectable BOOL isChecked;
#end
CheckBoxButton.m file content
#import "CheckBoxButton.h"
#interface CheckBoxButton()
#property(nonatomic,strong)IBInspectable UIImage* checkedStateImage;
#property(nonatomic,strong)IBInspectable UIImage* uncheckedStateImage;
#end
#implementation CheckBoxButton
-(id)init
{
self = [super init];
if(self)
{
[self addTarget:self action:#selector(switchState) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self addTarget:self action:#selector(switchState) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
[self addTarget:self action:#selector(switchState) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(void)setIsChecked:(BOOL)isChecked
{
_isChecked = isChecked;
if(isChecked)
{
[self setImage:self.checkedStateImage forState:UIControlStateNormal];
}
else
{
[self setImage:self.uncheckedStateImage forState:UIControlStateNormal];
}
}
-(void)switchState
{
self.isChecked = !self.isChecked;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
#end
You can set images for checked/unchecked as well as isChecked property in the attribute inspector of visual studio.
To add CheckBoxButton in storyboard or xib, simple add UIButton and set custom class like on next image.
Button will send UIControlEventValueChanged event, every time when isChecked state is changed.
Everyones code here is very long, slightly messy, and could be done a lot simpler. I have a project on GitHub that subclass UIControl that you can download and check out and gives you an almost native checkbox UI element:
https://github.com/Brayden/UICheckbox
I like the idea of Adrian to use the characters rather than images. But I don't like the box, it need only the checkmark itself (#"\u2713"). I draw a box (a rounded box) programmatically and place an UILabel contains the checkmark inside it. This way of implementation makes it easy to use the custom view in any application without care about any dependent resource. You can also customize the color of the checkmark, the rounded box and the background with ease.
Here's the complete code:
#import <UIKit/UIKit.h>
#class CheckBoxView;
#protocol CheckBoxViewDelegate
- (void) checkBoxValueChanged:(CheckBoxView *) cview;
#end
#interface CheckBoxView : UIView {
UILabel *checkMark;
bool isOn;
UIColor *color;
NSObject<CheckBoxViewDelegate> *delegate;
}
#property(readonly) bool isOn;
#property(assign) NSObject<CheckBoxViewDelegate> *delegate;
- (void) drawRoundedRect:(CGRect) rect inContext:(CGContextRef) context;
#end
#import "CheckBoxView.h"
#define SIZE 30.0
#define STROKE_WIDTH 2.0
#define ALPHA .6
#define RADIUS 5.0
#implementation CheckBoxView
#synthesize isOn, delegate;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, SIZE, SIZE)])) {
// Initialization code
}
//UIColor *color = [UIColor blackColor];
color = [[UIColor alloc] initWithWhite:.0 alpha:ALPHA];
self.backgroundColor = [UIColor clearColor];
checkMark = [[UILabel alloc] initWithFrame:CGRectMake(STROKE_WIDTH, STROKE_WIDTH, SIZE - 2 * STROKE_WIDTH, SIZE - 2*STROKE_WIDTH)];
checkMark.font = [UIFont systemFontOfSize:25.];
checkMark.text = #"\u2713";
checkMark.backgroundColor = [UIColor clearColor];
checkMark.textAlignment = UITextAlignmentCenter;
//checkMark.textColor = [UIColor redColor];
[self addSubview:checkMark];
[checkMark setHidden:TRUE];
isOn = FALSE;
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
CGRect _rect = CGRectMake(STROKE_WIDTH, STROKE_WIDTH, SIZE - 2 * STROKE_WIDTH, SIZE - 2*STROKE_WIDTH);
[self drawRoundedRect:_rect inContext:UIGraphicsGetCurrentContext()];
[checkMark setHidden:!isOn];
}
- (void)dealloc {
[checkMark release];
[color release];
[super dealloc];
}
- (void) drawRoundedRect:(CGRect) rect inContext:(CGContextRef) context{
CGContextBeginPath(context);
CGContextSetLineWidth(context, STROKE_WIDTH);
CGContextSetStrokeColorWithColor(context, [color CGColor]);
CGContextMoveToPoint(context, CGRectGetMinX(rect) + RADIUS, CGRectGetMinY(rect));
CGContextAddArc(context, CGRectGetMaxX(rect) - RADIUS, CGRectGetMinY(rect) + RADIUS, RADIUS, 3 * M_PI / 2, 0, 0);
CGContextAddArc(context, CGRectGetMaxX(rect) - RADIUS, CGRectGetMaxY(rect) - RADIUS, RADIUS, 0, M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + RADIUS, CGRectGetMaxY(rect) - RADIUS, RADIUS, M_PI / 2, M_PI, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + RADIUS, CGRectGetMinY(rect) + RADIUS, RADIUS, M_PI, 3 * M_PI / 2, 0);
CGContextClosePath(context);
CGContextStrokePath(context);
}
#pragma mark Touch
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint loc = [touch locationInView:self];
if(CGRectContainsPoint(self.bounds, loc)){
isOn = !isOn;
//[self setNeedsDisplay];
[checkMark setHidden:!isOn];
if([delegate respondsToSelector:#selector(checkBoxValueChanged:)]){
[delegate checkBoxValueChanged:self];
}
}
}
in .h file
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
BOOL isChecked;
UIImageView * checkBoxIV;
}
#end
And .m file
- (void)viewDidLoad
{
[super viewDidLoad];
isChecked = NO;
//change this property according to your need
checkBoxIV = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 15, 15)];
checkBoxIV.image =[UIImage imageNamed:#"checkbox_unchecked.png"];
checkBoxIV.userInteractionEnabled = YES;
UITapGestureRecognizer *checkBoxIVTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handlecheckBoxIVTapGestureTap:)];
checkBoxIVTapGesture.numberOfTapsRequired = 1;
[checkBoxIV addGestureRecognizer:checkBoxIVTapGesture];
}
- (void)handlecheckBoxIVTapGestureTap:(UITapGestureRecognizer *)recognizer {
if (isChecked) {
isChecked = NO;
checkBoxIV.image =[UIImage imageNamed:#"checkbox_unchecked.png"];
}else{
isChecked = YES;
checkBoxIV.image =[UIImage imageNamed:#"checkbox_checked.png"];
}
}
This will do the trick...
I made it with a UITextField to avoid drawing anything strange but I liked putting inside as text the tick unicode (Unicode Character 'CHECK MARK' (U+2713)) for the NSString: #"\u2713".
This way, in my .h file (implementing the protocol for the UITextField 'UITextFieldDelegate'):
UITextField * myCheckBox;
In my viewDidLoad or the function to prepare the UI:
...
myCheckBox = [[UITextField alloc] initWithFrame:aFrame];
myCheckBox.borderStyle = UITextBorderStyleRoundedRect; // System look like
myCheckBox.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
myCheckBox.textAlignment = NSTextAlignmentLeft;
myCheckBox.delegate = self;
myCheckBox.text = #" -"; // Initial text of the checkbox... editable!
...
Then, add an event selector for reating in the touch event and calling 'responseSelected' event:
...
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(checkboxSelected)];
[myCheckBox addGestureRecognizer:tapGesture];
...
Finally respond to that selector
-(void) checkboxSelected
{
if ([self isChecked])
{
// Uncheck the selection
myCheckBox.text = #" -";
}else{
//Check the selection
myCheckBox.text = #"\u2713";
}
}
The function 'isChecked' only checks if the text is the #"\u2713" check mark. To prevent showing the keyboard when the text field is selected use the event of the UITextField 'textFieldShouldBeginEditing' and add the event selector to manage the selection:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
// Question selected form the checkbox
[self checkboxSelected];
// Hide both keyboard and blinking cursor.
return NO;
}
Subclass UIButton, drop a button to view controller, select it and change class name to CheckBox in the identity inspector.
#import "CheckBox.h"
#implementation CheckBox
#define checked_icon #"checked_box_icon.png"
#define empty_icon #"empty_box_icon.png"
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setImage:[UIImage imageNamed:empty_icon] forState:UIControlStateNormal];
[self addTarget:self action:#selector(didTouchButton) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)didTouchButton {
selected = !selected;
if (selected)
[self setImage:[UIImage imageNamed:checked_icon] forState:UIControlStateNormal];
else
[self setImage:[UIImage imageNamed:empty_icon] forState:UIControlStateNormal];
}
#end
I made one recently. Free to acquire from GitHub. See if this will help. The effect is like
user Aruna Lakmal; FYI, when you add this code to IB as you describe initWithFrame is not called, initWithCoder is. Implement initWithCoder and it will work as you describe.
A simple UIButton subclass will able to behave like checkbox found in Android.
import UIKit
class CheckedUIButton: UIButton {
var checked: Bool = false {
didSet {
if checked {
setImage(UIImage(systemName: "checkmark.circle"), for: .normal)
} else {
setImage(UIImage(systemName: "circle"), for: .normal)
}
}
}
//initWithFrame to init view from code
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
//initWithCode to init view from xib or storyboard
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
addTarget(self, action: #selector(self.checkedTapped), for: .touchUpInside)
}
#objc
private func checkedTapped() {
self.checked.toggle()
}
}
Output
(Unchecked state)
(Checked state)
A simple UIButton subclass using SF symbols does the trick
class CheckBoxButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup() {
setImage(UIImage(systemName: "square.fill"), for: .normal)
setImage(UIImage(systemName: "checkmark.square.fill"), for: .selected)
}
}
Storyboard
If you are using storyboard, ensure button type is set to custom as seen in the image below
Usage
All you have to do is toggle the isSelected variable in action/selector method
#objc func toggle(_ sender: UIButton) {
sender.isSelected.toggle()
}
Demo

Resources