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;
}
I am new to XCode. I used with NSUserDefaults even though it is not coming what i required means If i click one time on that button it should change the colour to green and if i press it again it should change the colour to black.
Here my Code is -
- (IBAction)subscribeButtonAction:(id)sender {
if (count == 0) {
[_subscribeButtonObj setBackgroundColor:[UIColor greenColor]];
greenStr=[NSString stringWithFormat:#"green"];
NSUserDefaults *greendefults=[NSUserDefaults standardUserDefaults];
[greendefults setValue:greenStr forKey:#"greencolor"];
[greendefults synchronize];
///
count++;
} else if(count == 1){
[_subscribeButtonObj setBackgroundColor:[UIColor blueColor]];
blackStr=[NSString stringWithFormat:#"black"];
NSUserDefaults *blackDefaults=[NSUserDefaults standardUserDefaults];
[blackDefaults setValue:blackStr forKey:#"blackcolor"];
[blackDefaults synchronize];
//count = 0;
}
}
In ViewWillAppear I wrote the code like this -
-(void)viewWillAppear:(BOOL)animated
{
//count = 0;
if ([[NSUserDefaults standardUserDefaults] stringForKey:#"greencolor"]) {
NSLog(#"change the button to green color %#",[[NSUserDefaults standardUserDefaults] stringForKey:#"greencolor"]);
} else {
NSLog(#"change the button to blackcolor ");
}
}
Can anyone please help me. Thanks in Advance
There's no need of NSUserDefaults to implement this. Its simple to handle with UIButton's control states,
UIControlStateNormal
UIControlStateSelected
For example,
- (IBAction)changeColor:(id)sender {
UIButton *button = (UIButton *)sender;
if (button.selected) { // If selected will change the color into Red
button.backgroundColor = [UIColor redColor];
[button setSelected:NO];
} else {
button.backgroundColor = [UIColor greenColor];
[button setSelected:YES];
}
}
And, keeep in mind you can change your button's state initially like setting this,
[myButton setSelected:YES];
Assign, this code in your viewDidLoad
Cheers!!
If you wanna do it the NSUserDefault way. Setting control states is a nice way though. but It will set a unwanted overlay background to the button if UIControlStateSelected is YES. :
- (IBAction)btnAction:(id)sender {
NSUserDefaults *color=[NSUserDefaults standardUserDefaults];
if (count == 0) {
[self.btnOutlet setBackgroundColor:[UIColor greenColor]];
[color setValue:#"green" forKey:#"color"];
}else if(count == 1){
[self.btnOutlet setBackgroundColor:[UIColor blueColor]];
[color setValue:#"blue" forKey:#"color"];
}
count = !count;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
count = 0;
if([[[NSUserDefaults standardUserDefaults] valueForKey:#"color"] isEqualToString:#"green"]){ // been used
[self.btnOutlet setBackgroundColor:[UIColor greenColor]];
}else if([[[NSUserDefaults standardUserDefaults] valueForKey:#"color"] isEqualToString:#"blue"]){
[self.btnOutlet setBackgroundColor:[UIColor blueColor]];
}else{ // first time
[self.btnOutlet setBackgroundColor:[UIColor yellowColor]];
}
}
I'm trying to create a level select screen for a game I'm making which consists of a grid of circular buttons with level numbers in them. At any one time one button should be selected and displayed with a filled background instead of just an outline.
My initial implementation was to create a view which looked something like this:
#implementation LevelView
- (void)drawRect:(CGRect)rect {
int i = 1;
for (int row = 0; row < NUM_ROWS; row++) {
for (int col = 0; col < NUM_COLS; col++) {
// Calculate frame of the circle for this level.
if (i == selected) {
// Use CoreGraphics to draw filled circle with text.
}
else {
// Use CoreGraphics to draw outlined circle with text.
}
i++;
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
// Get the level that the touch is in the circle of.
int level = [self levelForPoint:[touch locationInView:self]];
selected = level;
[self setNeedsDisplay];
}
}
#end
The problem with this is that the selecting and unselecting of the various buttons are not animated (fade in, fade out) like the circular buttons in the iOS7 lock screen or Phone app.
Is there a better/easier way that I can accomplish this?
In response to your request to fade button state: we subclassed UIButton and overrode the setHighlighted: method like so:
- (void) setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
if (highlighted) {
[self setBackgroundColor:[UIColor whiteColor]];
} else {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.4f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self setBackgroundColor:nil];
[UIView commitAnimations];
}
}
As promised here is my way of tackling the problem. Let's first create a grid of UIButtons in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
for (NSUInteger i = 0; i < 10; ++i)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = i;
[button setImage:[UIImage imageNamed:#"normal_image"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"selected_image"] forState:UIControlStateSelected];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
//You can assign a button frame here or in viewWillLayoutSubviews or viewDidLayoutSubviews
button.frame = CGRectMake(your...frame);
[self.view addSubview:button];
[buttons addObject:button]; //NSMutableArray ivar containing our buttons
}
And that takes care of placing our buttons. You can use 2 for loops to make a grid of your choice. As for the image I'm assuming that you're using pre-baked images (you can pre-bake for retina and non retina separately). If you want to render it programatically let me know and I will edit the post.
As for the selection logic we implement the selector buttonTapped:
- (void)buttonTapped:(UIButton *)sender
{
for (UIButton *button in buttons) //Resets all selected states if there were any
button.state = UIControlStateNormal;
sender.state = UIControlStateSelected;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Hey" message:[NSString stringWithFormat:#"You touches the %ldth button.", (long)sender.tag + 1] delegate:nil cancelButtonTitle:#"Cool" otherButtonTitles:nil]
[alert show];
}
EDIT as for the animation purposes you can take a look at the link here or you can use an array of imageViews with attached UITapGestureRecognizers.
EDIT 2 A basic way on how to draw images for your buttons:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(buttonWidth, buttonHeight), NO, 0.0f); //Makes a graphics context that is not opaque and uses the screen scale (so you do not need to worry about retina or non retina)
[[UIColor redColor] set];
UIRectFill(CGRectMake(0.0f, 0.0f, buttonWidth, buttonHeight);
NSDictionary *drawingAttributes = #{NSFontAttributeName: [UIFont systemFontOfSize:15.0f], NSForegroundColorAttributeName: [UIColor blueColor]};
[#"Normal state" drawAtPoint:CGPointZero withAttributes:drawingAttributes];
UIImage *normalStateImate = UIGraphicsGetImageFromCurrentImageContext();
[[UIColor greenColor] set];
UIRectFill(CGRectMake(0.0f, 0.0f, buttonWidth, buttonHeight);
[#"Selected state" drawAtPoint:CGPointZero withAttributes:drawingAttributes];
UIImage *selectedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
And then you would apply the images in the same way as I have written above. How to draw pretty images and whatnot is another topic and if you want help on that I suggest googling or asking another question. If you ever feel like changing the images you would redraw them and apply them again to your buttons.
Below is the error i am getting in my app, which is working fine in ios6.
[__NSCFString frame]: unrecognized selector sent to instance 0xc075290
I am not getting what is wrong in it. But i guess something related to UINavigationController. Please guide for above.
Thanks in advance.
UPDATE: After enabling Zombies i get this error.
[_UINavigationBarBackIndicatorView frame]: message sent to deallocated instance 0xc0fb860
-(void)viewWillAppear:(BOOL)animated
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"isAcceptTerms"]) {
[adBannerView setDelegate:self];
[adBannerView setHidden:NO];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"isBannerShown"]) //-ive logic is applied
{
[self.adBannerView setHidden:YES];
[self.adBannerView setDelegate:nil];
}
}
else
{
[adBannerView setDelegate:nil];
[adBannerView setHidden:YES];
}
[self.navigationController.navigationBar setHidden:NO];
NSMutableDictionary *dictTemp =[[sqlmessenger shared]fetchOrders];
int count=[[sqlmessenger shared] isuserdetails];
if (count>0)
{
[self updateCoordinate];
}
NSArray *arrContorl = [self.navigationController.navigationBar subviews];
for(UIButton *btnTemp in arrContorl)
{
if([btnTemp isKindOfClass:[UIButton class]])
{
[btnTemp removeFromSuperview];
}
}
UIImageView *imgHeader= [[UIImageView alloc]initWithFrame:CGRectMake(0,0,320,44)];
[imgHeader setBackgroundColor:[UIColor clearColor]];
[imgHeader setImage:[UIImage imageNamed:#"setting.png"]];
[self.navigationController.navigationBar addSubview:imgHeader];
if(lblHeader)
{
lblHeader=nil ;
}
lblHeader = [[UILabel alloc]initWithFrame:CGRectMake(60,5,230,30)];
[lblHeader setBackgroundColor:[UIColor clearColor]];
[lblHeader setTextAlignment:UITextAlignmentLeft];
[lblHeader setTextColor:[UIColor whiteColor]];
[lblHeader setFont:[UIFont boldSystemFontOfSize:18.0]];
if([dictTemp count]==0 && contentView.hidden == FALSE)
{
[lblHeader setText:#"Terms of Service (EULA)"];
}
else
{
[lblHeader setFrame:CGRectMake(110,5,200,30)];
[lblHeader setFont:[UIFont boldSystemFontOfSize:20.0]];
[lblHeader setText:#"Settings"];
}
[self.navigationController.navigationBar addSubview:lblHeader];
}
Obviously, you are trying to access the frame property of a NSString object, which is not permitted, since this object does not have this property.
Try adding more details. (Add the code that causes the crash, usually crash stack are not that helpful).
UPDATE:
Still not sure what's going on, you need to do the actual debug, plant the necessary breakpoints log your variables, see what values they have etc.
I can give you some things you can try:
1.Not sure why are you adding subviews to the navigation bar. You can instead use the navigationItem property of the UIViewController, and then leftBarButtonItem of UINavigationItem, like :
For left bar button item : (make sure you hide the back button first)
self.navigationController.navigationItem.hidesBackButton = YES;
self.navigationItem.leftBarButtonItem = yourLeftBarButtonItem;
And for the right one :
self.navigationItem.rightBarButtonItem = yourRightBarButtonItem;
2.You're allocating the view and the label each time that viewController is appearing. That's inefficient. Both memory and time-wise. Instead, you can allocate them once and change the alpha channels.
Does anybody know of a way to customize the appearance of the string based UISegmentedControl? I am trying to set the background color of the cell and the text color differently depending on the selected state of the item.
Alternatively, do you know of a way to create UIImages on the fly in which to include custom strings? (e.g. create UUImage with white background, overlay text, add to segmented control).
I know that you can only have strings or images in the segmented control...
UISegmentedControl has a tintColor property -- this allows you to change what color the control is, but not the general "style" (the rounded, beveled shape):
segmentedControl.tintColor = [UIColor blueColor];
As for creating UIImages on the fly, you can create a CGContext, do whatever drawing you need to in that context (including strings), and then get a UIImage out of the context's CGImage:
CGContextRef drawContext = CGBitmapContextCreate(<many parameters>);
//do drawing here
CGImageRef finalImage = CGBitmapContextCreateImage(drawContext);
UIImage *cellImage = [UIImage finalImage];
Please note, that if you use code like UIView.appearance().tintColor = .myColor (or equiv. in ObjC), the effect most likely won't take place.
segmentedControl.tintColor = [UIColor colorWithRed:0.61176f green:0.61176f blue:0.61176f alpha:1.0f];
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
Most of the answers here don't answer the specific question of how to set a button color based on selected state which implies another color is desired for unselected state. I struggled with this for quite some time and wanted to share my solution for others to use.
My example uses a UISegmentedControl with three segments. The unselected color for all three should be the same to give it a uniform look. The selected state for the first and last segment have unique colors.
The issue is that the segmented control is not guaranteed to be in the same order so the colors will get mixed up as you select back and forth. Dan posted a solution that uses tags but unfortunately it's no longer guaranteed to work for iOS 6 and up.
Most of this code is taken from this post. I changed it slightly to have unique selected colors.
What makes it work is the sorting but take note of these 2 important lines for setting the selected color:
NSInteger selectedIdx = betterSegmentedControl.selectedSegmentIndex;
[[sortedViews objectAtIndex:selectedIdx] setTintColor:[self.segmentColors objectAtIndex:selectedIdx]];
- (void) updateSegmentColors
{
UIColor *checkColor = [UIColor colorWithRed: 29/255.0 green:166/255.0 blue:47/255.0 alpha:1.0];
NSArray *segmentColors = [[NSArray alloc] initWithObjects:checkColor, [UIColor blueColor], [UIColor redColor], nil];
UISegmentedControl *betterSegmentedControl = self.StatusControl;
// Get number of segments
NSUInteger numSegments = [betterSegmentedControl.subviews count];
// Reset segment's color (non selected color)
for( int i = 0; i < numSegments; i++ ) {
// reset color
[[betterSegmentedControl.subviews objectAtIndex:i] setTintColor:nil];
[[betterSegmentedControl.subviews objectAtIndex:i] setTintColor:[UIColor blueColor]];
}
// Sort segments from left to right
NSArray *sortedViews = [betterSegmentedControl.subviews sortedArrayUsingFunction:compareViewsByOrigin context:NULL];
// Change color of selected segment
NSInteger selectedIdx = betterSegmentedControl.selectedSegmentIndex;
[[sortedViews objectAtIndex:selectedIdx] setTintColor:[self.segmentColors objectAtIndex:selectedIdx]];
// Remove all original segments from the control
for (id view in betterSegmentedControl.subviews) {
[view removeFromSuperview];
}
// Append sorted and colored segments to the control
for (id view in sortedViews) {
[betterSegmentedControl addSubview:view];
}
}
NSInteger static compareViewsByOrigin(id sp1, id sp2, void *context)
{
// UISegmentedControl segments use UISegment objects (private API). But we can safely cast them to UIView objects.
float v1 = ((UIView *)sp1).frame.origin.x;
float v2 = ((UIView *)sp2).frame.origin.x;
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}
I placed the code in it's own method because I'm loading these segmented controls in a table view and need to run it upon loading (existing states from storage) and when a user changes a selection. Now I just need to call [Self updateSegmentColors]; when something changes.
All you have to do is:
// Get an array of the subviews of a UISegmentedControl, for example myUISegmentedControl:
NSArray *arri = [myUISegmentedControl subviews];
// Change the tintColor of each subview within the array:
[[arri objectAtIndex:0] setTintColor:[UIColor redColor]];
[[arri objectAtIndex:1] setTintColor:[UIColor greenColor]];
The best way I have found of doing something like this is setting different attributes for different UIControlStates on the segmented control.
self.segmentedControl.tintColor = [UIColor cb_Grey1Color];
self.segmentedControl.backgroundColor = [UIColor cb_Grey3Color];
NSDictionary *selectedAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont cbGothamBookFontWithSize:13.0], NSFontAttributeName,
[UIColor whiteColor], NSForegroundColorAttributeName,
[UIColor cb_Grey1Color], NSBackgroundColorAttributeName, nil];
[self.segmentedControl setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected];
NSDictionary *unselectedAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont cbGothamBookFontWithSize:13.0], NSFontAttributeName,
[UIColor cb_Grey2Color], NSForegroundColorAttributeName,
[UIColor cb_Grey3Color], NSBackgroundColorAttributeName, nil];
[self.segmentedControl setTitleTextAttributes:unselectedAttributes forState:UIControlStateNormal];
Font Color swift 3 and swift 4 if you want to change
For Unselected item
segcntrl.setTitleTextAttributes(titleTextAttributes, for: .normal)
For Selected item
segcntrl.setTitleTextAttributes(titleTextAttributes, for: .selected)
//MARK:- Segment color change
self.segc.setTitleTextAttributes([NSAttributedStringKey.foregroundColor:
UIColor.white], for: UIControlState.selected)
self.segc.setTitleTextAttributes([NSAttributedStringKey.foregroundColor:
UIColor.white], for: UIControlState.normal)
As of iOS13, you would be no longer able to modify the tint color of the segment controller. Need to use selectedSegmentTintColor if the color has to be customized.
self.yourSegmentControl.selectedSegmentTintColor = UIColor(red: 240.0/255.0, green: 183.0/255.0, blue: 0.0/255.0, alpha: 1.0)
Here is a sample code that works with iOS9, but it is a hack, and might not work in later versions:
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:#[#"Title1", #"Title2"]];
for (id segment in [segmentedControl subviews])
{
for (id view in [segment subviews])
{
NSString *desc = [view description];
if ([desc containsString:#"UISegmentLabel"])
{
[segment setTintColor:([desc containsString:#"Title1"] ? [UIColor blueColor] : [UIColor greenColor])];
}
}
}
I was able to do it via Interface Builder in XCode 6. Attached is the tint property:
I wanted to accomplish something similar - set the background color of the selected segment to one color while the 'outline' of the rest of the segments were a different color.
Borrowing heavily from Portland Runner's answer, the idea is to subclass the UISegmentedControl, and override 2 methods to both style the initial state as well as capture the change event to style it automatically as the user selects different segments.
- (void)layoutSubviews {
[super layoutSubviews];
[self updateSegmentColors];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self updateSegmentColors];
}
- (void)updateSegmentColors {
NSArray* segments = [self.subviews sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
// UISegmentedControl segments use UISegment objects (private API). But we can safely cast them to UIView objects.
float v1 = ((UIView *)obj1).frame.origin.x;
float v2 = ((UIView *)obj2).frame.origin.x;
if (v1 < v2) return NSOrderedAscending;
else if (v1 > v2) return NSOrderedDescending;
else return NSOrderedSame;
}];
for (int i=0; i<segments.count; i++) {
if (i == self.selectedSegmentIndex) {
[segments[i] setTintColor:[UIColor redColor]];
} else {
[segments[i] setTintColor:[UIColor grayColor]];
}
}
}
Swift 5.0
if #available(iOS 13.0, *) {
// For selected segment
control.selectedSegmentTintColor = UIColor.red
}
// For control's background
control.layer.backgroundColor = UIColor.black.cgColor
Storyboard Solution
Xcode 11
Select the control and multiple color options are now available. If you need further refinement - check out User Defined Runtime Attributes.