Change UIDatePicker font color? - ios

All I want to do is change the font color of the UIDatePicker. I've researched other questions but they're all involving changing other properties and customizing the entire look. All I want to do is just change the font color from black to white. I find it hard to believe that I can't do such a seemingly simple task. And why wouldn't the tint color affect it? Does it even do anything?

All I need (on iOS 8.x and 9.0) is this one line to change the font color:
[my_date_picker setValue:[UIColor whiteColor] forKey:#"textColor"];
No subclassing or invoking of private APIs...
Note: today's current date will still be highlighted (in newer iOS versions). You can get around this by using the following code:
if ([my_date_picker respondsToSelector:sel_registerName("setHighlightsToday:")]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
[my_date_picker performSelector:#selector(setHighlightsToday:) withObject:[NSNumber numberWithBool:NO]];
#pragma clang diagnostic pop
}

As of Swift 2.1:
picker.setValue(UIColor.whiteColor(), forKey: "textColor")
picker.sendAction("setHighlightsToday:", to: nil, forEvent: nil)
let date = NSDate()
picker.setDate(date, animated: false)
Instead of "today" you will see the current day,

One other alternative to #Jeremiah answer is to define those values in Interface Builder.
I prefer this one because there is no code to add in your ViewController.
Click into your DatePicker view and customise it from the Attribute inspector as in the screenshot.
Works for Swift 2, 3, 4 and probably for Swift < 2. (not tested)

Next solution comes from "arturgrigor" and it works great in my apps, just copy it, paste it in viewDidLoad method, and enjoy it :
[my_datePicker setValue:[UIColor whiteColor] forKeyPath:#"textColor"];
SEL selector = NSSelectorFromString( #"setHighlightsToday:" );
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature :
[UIDatePicker
instanceMethodSignatureForSelector:selector]];
BOOL no = NO;
[invocation setSelector:selector];
[invocation setArgument:&no atIndex:2];
[invocation invokeWithTarget:my_datePicker];

According to Apple's UIKit User Interface Catalog, developers are not allowed to customize date pickers.
I've seen other StackOverflow answers for similar questions that suggest making a fake UIDatePicker using UIPickerView and customizing that.
I also found an open source date picker on GitHub (at https://github.com/mwermuth/MWDatePicker ) that might help a bit. It allows for different background and selector styles, but not a different font or font attributes.... yet.

To change UIDatePicker text color use:
// MARK: - Helping content
private enum DatePickerProperties: String {
case TextColor = "textColor"
case HighlightsToday = "highlightsToday"
}
// MARK: - Outlets
#IBOutlet private weak var datePicker: UIDatePicker!
// MARK: - Lifecicle
public override func awakeFromNib() {
super.awakeFromNib()
self.datePicker.setValue(UIColor.whiteColor(), forKey: DatePickerProperties.TextColor.rawValue)
self.datePicker.setValue(false, forKey: DatePickerProperties.HighlightsToday.rawValue)
}
It works like a charm with xCode 7.3 and Swift 2.3.

I stumbled upon a surprisingly clean solution using UIAppearance, without using any KVC, swizzling, or otherwise private API. I found that attempting to set the textColor via UIAppearance for any UILabel within a UIDatePicker had no affect, but a custom appearance property that simply called the regular textColor setter worked just fine.
// Implement a custom appearance property via a UILabel category
#interface UILabel (PickerLabelTextColor)
#property (nonatomic, strong) UIColor * textColorWorkaround UI_APPEARANCE_SELECTOR;
#end
#implementation UILabel (PickerLabelTextColor)
- (UIColor *)textColorWorkaround {
return self.textColor;
}
- (void)setTextColorWorkaround:(UIColor *)textColor {
self.textColor = textColor;
}
#end
And then use as follows:
UILabel *pickerLabelProxy = [UILabel appearanceWhenContainedInInstancesOfClasses:#[UIDatePicker.class]];
pickerLabelProxy.textColorWorkaround = UIColor.lightGrayColor;
Swift Version
UILabel extension:
extension UILabel {
#objc dynamic var textColorWorkaround: UIColor? {
get {
return textColor
}
set {
textColor = newValue
}
}
}
Appearance proxy use:
let pickerLabelProxy = UILabel.appearance(whenContainedInInstancesOf: [UIDatePicker.self])
pickerLabelProxy.textColorWorkaround = UIColor.lightGray

If anyone wants the swift solution, I placed the following in viewDidLoad:
birthdayDatePicker.setValue(DesignHelper.getOffWhiteColor(), forKey: "textColor")
birthdayDatePicker.performSelector("setHighlightsToday:", withObject:DesignHelper.getOffWhiteColor())

For Xamarin developers:
DatePicker.SetValueForKey(UIColor.White, new NSString("textColor"));
DatePicker.SetValueForKey(FromObject(false), new NSString("highlightsToday"));
It´s working like a charm.
Tested in iOS 9 and 10

You can also add this as an IBDesignable if you want to configure this within InterFace Builder.
import UIKit
#IBDesignable
extension UIDatePicker {
#IBInspectable var textLabelColor: UIColor? {
get {
return self.valueForKey("textColor") as? UIColor
}
set {
self.setValue(newValue, forKey: "textColor")
self.performSelector("setHighlightsToday:", withObject:newValue) //For some reason this line makes the highlighted text appear the same color but can not be changed from textColor.
}
}
}

This subclass of UIDatePicker works for iOS 7. Its not pretty but gets the job done.
#define kNotification_UIView_didAddSubview #"kNotification_UIView_didAddSubview"
#implementation UIView (addSubview)
-(void) didAddSubview:(UIView *)subview{
[[NSNotificationCenter defaultCenter] postNotificationName:kNotification_UIView_didAddSubview object:self];
}
#end
#interface DatePicker ()
#property (nonatomic, strong) UIColor* textColor;
#end
#implementation DatePicker
-(id) initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self){
[self setup];
}
return self;
}
-(id) initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self){
[self setup];
}
return self;
}
-(void) setup{
self.textColor = [UIColor darkTextColor];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(subviewsUpdated:) name:kNotification_UIView_didAddSubview object:nil];
}
-(void) dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void) updateLabels:(UIView*) view{
for (UILabel* label in view.subviews){
if ([label isKindOfClass:[UILabel class]]){
label.textColor = self.textColor;
}else{
[self updateLabels:label];
}
}
}
-(BOOL) isSubview:(UIView*) view{
if (view == nil){
return NO;
}
if (view.superview == self){
return YES;
}
return [self isSubview:view.superview];
}
-(void) subviewsUpdated:(NSNotification*) notification{
if ([notification.object isKindOfClass:NSClassFromString(#"UIPickerTableView")] && [self isSubview:notification.object]){
[self updateLabels:notification.object];
}
}
#end

[date_picker setValue:textColor forKey:#"textColor"];
[date_picker performSelector:#selector(setHighlightsToday:) withObject:NO];

Add Runtime Attribute named "textColor" from Storyboard as shown in following image.

As alternative to #Jeremiah answer, you can use this:
datePicker.setValue(UIColor.redColor(), forKey: "textColor")
datePicker.sendAction("setHighlightsToday:", to: nil, forEvent: nil)
datePicker.setDate(NSDate(timeIntervalSinceReferenceDate: 0), animated: false)
it will remove Today (you will see current date), but it will be with right color.
Possible troubles:
if you change color dynamically, I didn't find a way to reload date picker. So, the user will see previous color and only after scroll, color will changed to a new one. -> Solved, by last string. Looks like Voodoo, but it works...
This answer suitable for Swift 2.1

It didn't work until textColor was set inside layoutSubviews()
override func layoutSubviews() {
super.layoutSubviews()
datePicker.backgroundColor = .black
datePicker.setValue(.white, forKeyPath: "textColor")
}

Just use datePicker.tintColor = .red or any other color you want.

[my_date_picker setValue:[UIColor whiteColor] forKey:#"textColor"];
it seems don't work on ios 13 or later, you can set overrideUserInterfaceStyle property for UIDatePicker to determin it shows white color or black.

Related

How to bind with struct in ReactiveCocoa with Objective-C

I will need to change the border color when meet certain condition like so, but I can't find a way to go through the compiler:
RAC(self.addrTextField.layer, borderColor) = [validateAddressSignal map:^ CGColorRef* (NSArray *array) {
if (array.count) {
return [UIColor greenColor].CGColor;
}
return [UIColor clearColor].CGColor;
}];
The most elegant solution to a similar question asked on Reactive Cocoa's GitHub Issue tracker is the following posted by Erik Price:
#interface UIView (MyCategoryName)
- (void)setMyBorderColor:(UIColor *)color;
#end
#implementation UIView
- (void)setMyBorderColor:(UIColor *)color
{
self.layer.borderColor = color.CGColor;
}
#end
// ...
RAC(myTextField, myBorderColor) = mySignalOfUIColors;
Basically, make it easy to bind the color by adding a category to UIView.

How to make UIbutton only appear when text is written?

I'm making a word game, and ive called my custom keyboards textfield _textbox
Ive put a x button that represents "clear written text" and I only need it to appear when the user types letters into the textfield!
Then disappear after the letters were cleared!
code:
- (IBAction)btnclear:(id)sender {
NSString *oldString = _textbox.text;
NSString *newString;
newString = [oldString substringFromIndex: _textbox.text.length];
[_textbox setText:newString];
}
The image is on the button!
If you're using a UITextField you can use the standard clear button with:
_textbox.clearButtonMode = UITextFieldViewModeWhileEditing;
If you're wanting a custom appearance to the button you can use rightView and rightViewMode to manage the state for you.
Use the following code, it uses UITextFieldTextDidChangeNotification notification,which is called every time you change text in your textfield, and hides or shows your button depending on input text.
- (void) viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textDidChange:) name:UITextFieldTextDidChangeNotification object: _textbox];
}
- (void) textDidChange:(NSNotification *)notification
{
UITextField *tf = (UITextField*)notification.object;
_button.hidden = (tf.text.length == 0);
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object: _textbox];
}
With the property "hidden" of the UIButton you can hide it
Check if there is text on your textView, and then hide your button
Use UITextFielDelegate method
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if(textField.text.length==0){
textXclear.hidden = NO;
}else{
textXclear.hidden = YES;
}
}
There are two ways, and by hidden do you mean not visible or just disabled?
For not visible, use the button.hidden property. For disabled (meaning it can't be touched), use the button.enabled property.
As for the textfield you could do something like this:
if ([textfield.text length] > 0) {...} else {...}
//extra stuff and suggestions
Also if you are using the text in the textfield to be added to some other view (say its an add item screen), you have to create a #property regarding the added item. And then you could, rather than the aforementioned mention write the code like in the .m:
if (self.aProperty != nil) {
button.hidden = NO;
} else {
button.hidden = YES;
And you'd have to declare the property in the .h file:
#property (nonatomic, strong) ObjectYouAreUsing *aProperty;
And this may be the reason it's not working but create a new file with the NSObject subclass. This will be the ObjectYouAreUsing.
This way you can access the pure object you are using and just import it where ever you need it. Also with this, if the user were to close the screen you could then write the initWithCoder method.

UITextView style is being reset after setting text property

I have UITextView *_masterText and after call method setText property font is being reset.
It's happening after I change sdk 7.
_masterText is IBOutlet, global and properties are set in storyboard. It's only me or this is general SDK bug?
#interface myViewController : UIViewController
{
IBOutlet UITextView *_masterText;
}
#implementation myViewController
-(void)viewWillAppear:(BOOL)animated
{
[_masterText setText:#"New text"];
}
Sitting with this for hours, I found the bug.
If the property "Selectable" = NO it will reset the font and fontcolor when setText is used.
So turn Selectable ON and the bug is gone.
I ran into the same issue (on Xcode 6.1) and while John Cogan's answer worked for me, I found that extending the UITextView class with a category was a better solution for my particular project.
interface
#interface UITextView (XcodeSetTextFormattingBugWorkaround)
- (void)setSafeText:(NSString *)textValue;
#end
implementation
#implementation UITextView (XcodeSetTextFormattingBugWorkaround)
- (void)setSafeText:(NSString *)textValue
{
BOOL selectable = [self isSelectable];
[self setSelectable:YES];
[self setText:textValue];
[self setSelectable:selectable];
}
#end
If you want your text view to be "read only" you can check Editable and Selectable and uncheck User Interaction Enabled, with this the UITextView was behaving as I wanted
Had this issue myself and the above answer helped but I added a wrapper to my ViewController code as follows and just pass the uiview instance and text to change and the wrapper function toggles the Selectable value on, changes text and then turns it off again. Helpful when you need the uitextview to be off at all times by default.
/*
We set the text views Selectable value to YES temporarily, change text and turn it off again.
This is a known bug that if the selectable value = NO the view loses its formatting.
*/
-(void)changeTextOfUiTextViewAndKeepFormatting:(UITextView*)viewToUpdate withText:(NSString*)textValue
{
if(![viewToUpdate isSelectable]){
[viewToUpdate setSelectable:YES];
[viewToUpdate setText:textValue];
[viewToUpdate setSelectable:NO];
}else{
[viewToUpdate setText:textValue];
[viewToUpdate setSelectable:NO];
}
}
EDIT :
Setting font for UITextView in iOS 7 work for me if firstly you set the text and after that you set the font :
#property (nonatomic, weak) IBOutlet UITextView *masterText;
#implementation myViewController
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
_myTextView.text = #"My Text";
_myTextView.font = [UIFont fontWithName:#"Helvetica.ttf" size:16]; // Set Font
}
On a XIB file, if you add some text in your UITextView and change the font or the color it will work.
Here's a quick subclass solution I often use for this problem.
class WorkaroundTextView: UITextView {
override var text: String! {
get {
return super.text
}
set {
let originalSelectableValue = self.selectable
self.selectable = true
super.text = newValue
self.selectable = originalSelectableValue
}
}
}
This issue resurfaced in Xcode 8. This is how I fixed it:
Changed the extension to:
extension UITextView{
func setTextAvoidXcodeIssue(newText : String, selectable: Bool){
isSelectable = true
text = newText
isSelectable = selectable
}
}
and checked the Selectable option in the Interface Builder.
It's not very elegant to have that 'selectable' parameter but it'll do.
In iOS 8.3, the workaround of setting "selectable" to YES before the setText, and NO after, didn't fix it for me.
I found I needed to set "selectable" to YES in the storyboard, too, before this would work.
This worked for me:
let font = textView.font
textView.attributedText = attributedString
textView.font = font
For me with attributed text, I just needed to set the font in the attributes dictionary rather than setting it in it's own field.
I am having this problem to.
A swifty-friendly solution of #Ken Steele's answer answer.
I extend the UITextView and add a computed property.
extension UITextView {
// For older Swift version output should be NSString!
public var safeText:String!
{
set {
let selectable = self.selectable;
self.selectable = true;
self.text = newValue;
self.selectable = selectable;
}
get {
return self.text;
}
}
}
hope it helps.
Its been 3 years and the bug still exists in the latest stable version of Xcode (7.3). Clearly apple wont be fixing it any time soon leaving developers with two options: leaving selectable on and setting UserInteractionEnabled to false or Method swizzling.
If you have a button on your textView the former will not suffice.
No-code-change-requied solution in swift:
import UIKit
extension UITextView {
#nonobjc var text: String! {
get {
return performSelector(Selector("text")).takeUnretainedValue() as? String ?? ""
} set {
let originalSelectableValue = selectable
selectable = true
performSelector(Selector("setText:"), withObject: newValue)
selectable = originalSelectableValue
}
}
}
Objective-C:
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
#implementation UITextView (SetTextFix)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = #selector(setText:);
SEL swizzledSelector = #selector(xxx_setText:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)xxx_setText:(NSString *)text {
BOOL originalSelectableValue = self.selectable;
self.selectable = YES;
[self xxx_setText:text];
self.selectable = originalSelectableValue;
}
#end
Using the work-around discussed in this issue, this extension to UITextView provides a setTextInCurrentStyle() function. Based on solution by Alessandro Ranaldi but does not require the current isSelectable value to be passed to the function.
extension UITextView{
func setTextInCurrentStyle(_ newText: String) {
let selectablePreviously = self.isSelectable
isSelectable = true
text = newText
isSelectable = selectablePreviously
}
}

Setting the properties of items in an customview in iOS

I am creating a customView and for example, would like to set the text color of the label and initialise the text of the label.
The initWithFrame is the generic function.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
I have tried initialising the label in the initWithFrame, but it doesn't work. But when I do it in awakeFromNib, it allows me to set the text color but not the text(this value comes from a dictionary)
-(void)awakeFromNib
{
//setting of the label textcolor
}
What would be the correct way to initialise the color and text of labels and other stuff?
Need some suggestions and guidance...
Edit:
-(void)viewDidLoad
{
[self updateViewWithDictionary:dictPassed];
}
Something like this?
What I do in some project's is expose a public method like so:
- (void)updateViewWithDictionary:(NSDictionary *)confDictionary;
In that dictionary I pass the parameters I want, and inside the UIView's subview I update it according to what I want.
Edit 1:
Read wrongly your question, sorry. You have a custom UIView that you would like to be updated when your UIViewController starts, or when you actually use it. So you should have something like this:
#interface MyView : UIView
{
}
- (void)updateViewWithDictionary:(NSDictionary *)confDictionary;
#end
And from your viewDidLoad:
-(void)viewDidLoad
{
[self.customView updateViewWithDictionary:dictPassed];
}

how to get selected text from uitextfield in iphone?

I am working on Text to speech application in iPhone,
in which have a text field that takes input, i want user to select some portion of text from text field and my application will convert that selected text into speech.
my problem is how would i get the text that user has selected from text field?
-[UITextField selectedText]
Although UITextField doesn't have a selectedText method, it conforms to the UITextInput protocol. So, you can use the required properties & methods of the UITextInput protocol to determine the selectedText of a UITextField *textField (or any object that conforms to the UITextInput protocol, such as a UITextView).
NSString *selectedText = [textField textInRange:textField.selectedTextRange];
NSLog(#"selectedText: %#", selectedText);
As an aside, you can also use the required properties & methods of the UITextInput to calculate the selectedRange of a UITextField *textField.
UITextRange *selectedTextRange = textField.selectedTextRange;
NSUInteger location = [textField offsetFromPosition:textField.beginningOfDocument
toPosition:selectedTextRange.start];
NSUInteger length = [textField offsetFromPosition:selectedTextRange.start
toPosition:selectedTextRange.end];
NSRange selectedRange = NSMakeRange(location, length);
NSLog(#"selectedRange: %#", NSStringFromRange(selectedRange));
-[UITextFieldDelegate textFieldDidChangeSelection:]
Although, UITextFieldDelegate doesn't declare a textFieldDidChangeSelection: delegate method like -[UITextViewDelegate textViewDidChangeSelection:], you can still hook into when the selection of a UITextField has changed. To do so, subclass UITextField and use method swizzling to add your own code to the textField.inputDelegate's native implementation of -[UITextInputDelegate selectionDidChange:].
// MyTextField.h
#import <UIKit/UIKit.h>
#interface MyTextField : UITextField
#end
// MyTextField.m
#import <objc/runtime.h>
#import "MyTextField.h"
UIKIT_STATIC_INLINE void mySelectionDidChange(id self, SEL _cmd, id<UITextInput> textInput);
#implementation MyTextField {
BOOL swizzled;
}
#pragma mark - UIResponder
// Swizzle here because self.inputDelegate is set after becomeFirstResponder gets called.
- (BOOL)becomeFirstResponder {
if ([super becomeFirstResponder]) {
[self swizzleSelectionDidChange:YES];
return YES;
} else {
return NO;
}
}
// Unswizzle here because self.inputDelegate may become the inputDelegate for another UITextField.
- (BOOL)resignFirstResponder {
if ([super resignFirstResponder]) {
[self swizzleSelectionDidChange:NO];
return YES;
} else {
return NO;
}
}
#pragma mark - Swizzle -[UITextInput selectionDidChange:]
// Swizzle selectionDidChange: to "do whatever you want" when the text field's selection has changed.
// Only call this method on the main (UI) thread because it may not be thread safe.
- (void)swizzleSelectionDidChange:(BOOL)swizzle {
if (swizzle == swizzled || ![self respondsToSelector:#selector(inputDelegate)]) return; // 4.3
Class inputDelegateClass = object_getClass(self.inputDelegate);
SEL mySelector = #selector(mySelectionDidChange:);
class_addMethod(inputDelegateClass, mySelector, (IMP)mySelectionDidChange, "v#:#");
Method myMethod = class_getInstanceMethod(inputDelegateClass, mySelector);
Method uiKitMethod = class_getInstanceMethod(inputDelegateClass, #selector(selectionDidChange:));
method_exchangeImplementations(uiKitMethod, myMethod);
swizzled = swizzle;
// NSLog(#"swizzled? %i", method_getImplementation(uiKitMethod) == (IMP)venmo_selectionDidChange);
}
#end
UIKIT_STATIC_INLINE void mySelectionDidChange(id self, SEL _cmd, id<UITextInput> textInput) {
// Call the native implementation of selectionDidChange:.
[self performSelector:#selector(mySelectionDidChange:) withObject:textInput];
// "Do whatever you want" with the selectedText below.
NSString *selectedText = [textInput textInRange:textInput.selectedTextRange];
NSLog(#"selectedText: %#", selectedText);
}
I did solve my query as follow :
I implement UITextView's delegate and implement following method
- (void)textViewDidChangeSelection:(UITextView *)textView {
NSRange r = textView.selectedRange;
NSLog(#"Start from : %d",r.location); //starting selection in text selection
NSLog(#"To : %d",r.length); // end position in text selection
NSLog([tv.text substringWithRange:NSMakeRange(r.location, r.length)]); //tv is my text view
}
That's it!
Swift
In Swift, getting the selected text from a UITextField is done like this:
if let textRange = myTextField.selectedTextRange {
let selectedText = myTextField.textInRange(textRange)
}
where textRange is a UITextRange that is used to get the actual selected text.
A similar topic is discussed here: Can I select a specific block of text in a UITextField?
AFAIK there is no event if text is selected. However, you could setup an NSTimer to watch your textfield and check the _selectedRange. If it changes, go fire up your text-to-speech code.
EDIT: I was wrong about the selection. UITextField cannot do what you want to achieve. But if you use UITextView instead, you can implement its UITextViewDelegate and override
- (void)textViewDidChangeSelection:(UITextView *)textView
In there, you can use the selectedRange poperty to get the selection. See this reference for details:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextView_Class/Reference/UITextView.html#//apple_ref/doc/uid/TP40006898-CH3-SW13
UITextField don't have delegate to get the selection range change. We can use KVO to observe selectedTextRange property of UITextfield.
[textField addObserver:self forKeyPath:#"selectedTextRange" options:NSKeyValueObservingOptionNew context:NULL];
Or create UITextField subclass and override setSelectedTextRange method.

Resources