iOS 13 - UITextField with Placeholder getting app crash - uitextfield

In iOS 13, I'm getting a crash when accessing the UITextField _placeholderLabel.textColor label key.
The key used to apply placeholder text color.
[textfield setValue:[UIColor whiteColor] forKeyPath:#"_placeholderLabel.textColor"];
"NSGenericException" - reason: "Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug"

You can do it by using runtime:
add the following code to the bottom of placeholder setting
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(textField, ivar);
placeholderLabel.textColor = [UIColor whiteColor];
At Xcode 11 beta2 ,this code is work ,but I don't know about GM version or official version.
The complete code:
Objective-C Version
#import "ViewController.h"
#import <objc/runtime.h>
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
self.title = #"UITextField Demo";
UITextField *textField = [UITextField new];
textField.frame = CGRectMake(0, 100, 300, 50);
textField.placeholder = #"UITextField Demo";
[self.view addSubview:textField];
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(textField, ivar);
placeholderLabel.textColor = [UIColor whiteColor];
}
#end
Swift Version:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
let textField = UITextField()
textField.frame = CGRect(x: 0, y: 100, width: 300, height: 50)
textField.placeholder = "UITextField Demo"
view.addSubview(textField)
let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(textField, iVar) as! UILabel
placeholderLabel.textColor = .red
}
}
2019/09/25 Update
The above implementation can solve the problem ,but it not be advocated.
The apps that use the private api maybe broken in the future.
Please use new api :
var attributedPlaceholder: NSAttributedString? { get set }
Discussion
This property is nil by default. If set, the placeholder string is drawn using system-defined color and the remaining style information (except the text color) of the attributed string. Assigning a new value to this property also replaces the value of the placeholder property with the same string data, albeit without any formatting information. Assigning a new value to this property does not affect any other style-related properties of the text field.
The complete code:
let textField = UITextField()
textField.frame = CGRect(x: 0, y: 100, width: 300, height: 50)
let placeholderString = NSAttributedString.init(string: "UITextField Demo", attributes: [NSAttributedString.Key.foregroundColor : UIColor.red])
textField.attributedPlaceholder = placeholderString
view.addSubview(textField)

I think it's a bug from the XCode side and hopefully, they will fix this one on the next release. You can quickly fix this by Erase all Contents and Settings on the Simulator device.

Yes this problem is related with the Xcode version. I have Xcode 11.2.1 and i am facing same issue.
_placeholderLabel.textColor
if you are using it from storyboard as runtime var and getting issue so just remove "_"
like that
after removing the "_" runtime Var start working

Remove Underscore "_" and try this.
[textfield setValue:[UIColor whiteColor] forKeyPath:#"placeholderLabel.textColor"];
It should work for all versions!

I did it this way :
Objc
NSMutableAttributedString *placeholderAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:searchField.attributedPlaceholder];
[placeholderAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [placeholderAttributedString length])];
searchField.attributedPlaceholder = placeholderAttributedString;
Swift 5
var placeholderAttributedString = NSMutableAttributedString(attributedString: searchField.attributedPlaceholder)
placeholderAttributedString.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 0, length: placeholderAttributedString.length))
searchField.attributedPlaceholder = placeholderAttributedString
It's a bit long but I have nothing better for the moment.

if you are using UITextView+Placeholder.h class please change below method Clean and build if issue persists
Objective C
+(UIColor *)defaultPlaceholderColor {
static UIColor *color = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
UITextField *textField = [[UITextField alloc] init];
textField.placeholder = #" ";
// color = [textField valueForKeyPath:#"_placeholderLabel.textColor"];
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(textField, ivar);
color = placeholderLabel.textColor;
});
return color;
}

You can set UITextField placeholder text color, font using attributedPlaceholder method as follows :
NSString *text = [textField text];
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:text attributes:#{NSForegroundColorAttributeName: [UIColor redColor], NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue-Medium" size:14.0f]}];

"Erase all contents and settings"
This worked for me.

I removed the var "_placeholderLabel" in the section "User Defined Runtime Attributes"

Just replace _placeholderLabel with placeholderLabel

For IOS 13 you can set UITextField placeholder color by one line code.
Objective C
[textField setAttributedPlaceholder:[[NSAttributedString alloc] initWithString:#"PlaceHolder Text" attributes:#{NSForegroundColorAttributeName:[UIColor whiteColor]}]];
In Swift 5
txtTitle.attributedPlaceholder = NSAttributedString(string:"PlaceHolder Text", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])

- Swift Version
Add the following code to the bottom of placeholder setting:
let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(textField, iVar) as! UILabel
placeholderLabel.textColor = .white
For example:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
let textField = UITextField()
textField.frame = CGRect(x: 0, y: 100, width: 300, height: 50)
textField.placeholder = "UITextField Demo"
view.addSubview(textField)
let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(textField, iVar) as! UILabel
placeholderLabel.textColor = .white
}

Swift 5
Here is a correct Swift replacement of that property.
Note that if you also change alignment of other properties for the text field, then you need to set these properties to placeholder's attributed text too. Usually only color and font are changed:
/// Set placeholder text color
/// - Parameter color: the color
func setPlaceholderColor(_ color: UIColor) {
// Color
var attributes: [NSAttributedString.Key: Any] = [.foregroundColor: color]
var range = NSRange(location: 0, length: 1)
// Font
if let text = attributedText, text.length > 0, let attrs = attributedText?.attributes(at: 0, effectiveRange: &range), let font = attrs[.font] {
attributes[.font] = font
}
else if let font = font {
attributes[.font] = font
}
self.attributedPlaceholder = NSAttributedString(string: self.placeholder ?? "", attributes: attributes)
}

FOR UISEARCHBAR PLACEHOLDER COLOR
Xcode 11.1
From iOS 13 onwards the SDK provides UISearchBar.searchTextField so we can use
public API instead of private API. In the subclass of UISearchBar i have used this code to change the placeholder color
UITextField *textField = [self findSubviewOfClass:[UITextField class]];
textField.textColor = [UIColor whiteColor];
//textField.font = [UIFont fontWithName:#"HelveticaNeue-Regular" size:14.0f];
if (#available(iOS 13.0, *)) {
self.searchTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:#{NSForegroundColorAttributeName: [UIColor colorWithRed:0.8 green:0.82 blue:0.81 alpha:1]}];
}else {
[textField setValue:[UIColor colorWithRed:0.8 green:0.82 blue:0.81 alpha:1] forKeyPath:#"_placeholderLabel.textColor"];
}

Remove "_" form "_placeholderLabel.textColor" try with "placeholderLabel.textColor".

Related

How to set up an NSTextView programmatically with explicit NSLayoutManager, NSTextStorage, NSTextContainer?

Following the apple documentation I am trying to set up a simple NSTextView via its two constructor methods.
I am placing the below code inside the viewDidAppear method of the view controller of the content view. textView is an instance of NSTextView, frameRect is the frame of the content view.
The following Swift code works (gives me an editable textView with the text showing on the screen):
textView = NSTextView(frame: frameRect!)
self.view.addSubview(textView)
textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello"))
The following does NOT work (text view is not editable and no text shown on the screen):
var textStorage = NSTextStorage()
var layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
var textContainer = NSTextContainer(containerSize: frameRect!.size)
layoutManager.addTextContainer(textContainer)
textView = NSTextView(frame: frameRect!, textContainer: textContainer)
textView.editable = true
textView.selectable = true
self.view.addSubview(textView)
textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello more complex"))
What am I doing wrong in the second example? I am trying to follow the example given in Apple's "Cocoa Text Architecture Guide" where they discuss setting up an NSTextView by explicitly instantiating its web of helper objects.
You need to keep a reference to the NSTextStorage variable you create. I'm not quite sure about the mechanics of it all, but it looks like the text view only keeps a weak reference to its text storage object. Once this object goes out of scope, it's no longer available to the text view. I guess this is in keeping with the MVC design pattern, where views (the NSTextView in this case) are meant to be independent of their models (the NSTextStorage object).
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var textView: NSTextView!
var textStorage: NSTextStorage! // STORE A REFERENCE
func applicationDidFinishLaunching(aNotification: NSNotification) {
var view = window.contentView as NSView
textStorage = NSTextStorage()
var layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
var textContainer = NSTextContainer(containerSize: view.bounds.size)
layoutManager.addTextContainer(textContainer)
textView = NSTextView(frame: view.bounds, textContainer: textContainer)
textView.editable = true
textView.selectable = true
view.addSubview(textView)
textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello more complex"))
}
}
Tested under Xcode 12.4. in Playgrounds:
import Cocoa
import AppKit
let textViewFrame = CGRect(x: 0, y: 0, width: 250, height: 90)
let textStorage = NSTextStorage()
var layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
var textContainer = NSTextContainer(containerSize: textViewFrame.size)
layoutManager.addTextContainer(textContainer)
let textView = NSTextView(frame: textViewFrame, textContainer: textContainer)
textView.isEditable = true
textView.isSelectable = true
textView.textColor = NSColor.red
textView.string = "Why is this so complicated..."
#import <Cocoa/Cocoa.h>
#interface TextViewController : NSObject {
NSLayoutManager *secondLayout;
IBOutlet NSSplitView *columnView;
IBOutlet NSTextView *bottomView;
}
- (IBAction) addColumn: (id)sender;
#end
#import "TextViewController.h"
#implementation TextViewController
- (void)awakeFromNib
{
NSTextStorage *storage = [bottomView textStorage];
secondLayout = [NSLayoutManager new];
[storage addLayoutManager: secondLayout];
[secondLayout release];
[self addColumn: nil];
[self addColumn: nil];
}
- (IBAction) addColumn: (id)sender
{
NSRect frame = [columnView frame];
NSTextContainer *container = [[NSTextContainer alloc]
initWithContainerSize: frame.size];
[container setHeightTracksTextView: YES];
[container setWidthTracksTextView: YES];
[secondLayout addTextContainer: container];
[container release];
NSTextView *newView = [[NSTextView alloc] initWithFrame: frame
textContainer: container];
[columnView addSubview: newView];
[newView release];
}
#end

How can I set the text color of an NSButton via Interface Builder?

There are several questions about how to set the text color programmatically. That's all fine, but there's got to be a way to do it via Interface Builder also.
The "Show Fonts" box works for changing the size of the button text, but Xcode ignores any color changes made using the widget there, and the Attributes Inspector for NSButton doesn't have a color picker...
I've no idea why this is missing still from NSButton. But here is the replacement class in Swift 4:
import Cocoa
class TextButton: NSButton {
#IBInspectable open var textColor: NSColor = NSColor.black
#IBInspectable open var textSize: CGFloat = 10
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func awakeFromNib() {
let titleParagraphStyle = NSMutableParagraphStyle()
titleParagraphStyle.alignment = alignment
let attributes: [NSAttributedStringKey : Any] = [.foregroundColor: textColor, .font: NSFont.systemFont(ofSize: textSize), .paragraphStyle: titleParagraphStyle]
self.attributedTitle = NSMutableAttributedString(string: self.title, attributes: attributes)
}
}
Try this solution,i hope so you will get :)
NSFont *txtFont = button.font;
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment:button.alignment];
NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSColor whiteColor], NSForegroundColorAttributeName, style, NSParagraphStyleAttributeName, txtFont, NSFontAttributeName, nil];
NSAttributedString *attrString = [[NSAttributedString alloc]
initWithString:button.title attributes:attrsDictionary];
[button setAttributedTitle:attrString];
You can also add this extension to your code if you like the 'Throw an extension in and look if it sticks' approach.
extension NSButton {
#IBInspectable open var textColor: NSColor? {
get {
return self.attributedTitle.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? NSColor
}
set {
var attributes = self.attributedTitle.attributes(at: 0, effectiveRange: nil)
attributes[.foregroundColor] = newValue ?? NSColor.black
self.attributedTitle = NSMutableAttributedString(string: self.title,
attributes: attributes)
}
}
}
Edit: Misread question. Below is how you'd change the text of a button on an iOS app.
Just to clarify, this isn't working for you?
added button
click on it and go to Attributes Inspector
change color in "Text Color" field

UITextField doesn't work

In a TabBar-Application I have on the view a small one, in which I can draw. Works perfect.
With that code:
UITextField * textFieldRounded = [[UITextField alloc] initWithFrame:CGRectMake(10, 45.0, 260.0, 50)];
textFieldRounded.borderStyle = UITextBorderStyleRoundedRect;
textFieldRounded.textColor = [UIColor blackColor]; //text color
textFieldRounded.font = [UIFont systemFontOfSize:17.0]; //font size
textFieldRounded.placeholder = #"<enter text>"; //place holder
textFieldRounded.backgroundColor = [UIColor whiteColor]; //background color
textFieldRounded.autocorrectionType = UITextAutocorrectionTypeNo; // no auto correction support
textFieldRounded.keyboardType = UIKeyboardTypeDefault; // type of the keyboard
textFieldRounded.returnKeyType = UIReturnKeyDone; // type of the return key
textFieldRounded.clearButtonMode = UITextFieldViewModeWhileEditing; // has a clear 'x' button to the right
textFieldRounded.userInteractionEnabled = YES;
[skizzenFeldOutlet addSubview:textFieldRounded];
textFieldRounded.delegate = self;
I create a textfield. It works fine, but I can't put text on the textfield. The textfield is visible but I can draw in the view under the textfield!
Any tips?
Best regards
Andreas
because of this.
[skizzenFeldOutlet addSubview:textFieldRounded];
your textField is been flat added in your view.

Selecting tabBarItem causes title to disappear

Like a title, i have a view build with IB. When the app are lunched all work fine, but when i select a tab bar item the title disappear!
Any idea?
sory for my bad english, i'm italian :D
UPDATE:
the title of the tabBarItem is taked by the viewController title, in this case
self.title = #""
i have to make hidden the viewcontroller title
This simple line will also do the job :
self.navigationItem.title = #"";
Cheers.
self.title = #"MY TITLE";
CGRect frame = CGRectMake(0, 0, 400, 44);
UILabel *label = [[[UILabel alloc] initWithFrame:frame] autorelease];
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor clearColor];
label.text = #"NO TEXT";
self.navigationItem.titleView = label;
(I tried self.navigationItem.titleView = nil or self.navigationItem.titleView.hidden = YES but without results.)

How to resize NSTextView according to its content?

I am trying to set an attributed string within NSTextView. I want to increase its height based on its content, initially it is set to some default value.
So I tried this method:
I set content in NSTextView. When we set some content in NSTextView its size automatically increases. So I increased height of its super view that is NSScrollView to its height, but NSScrollView is not getting completely resized, it is showing scroller on right.
float xCoordinate = 15.0;
[xContentViewScroller setFrame:NSMakeRect(xCoordinate, 0.0, 560.0, 10.0)];
[[xContentView textStorage] setAttributedString:xContents];
float xContentViewScrollerHeight = [xfContentView frame].size.height + 2;
[xContentViewScroller setFrame:NSMakeRect(xCoordinate, 0.0, 560.0, xContentViewScrollerHeight)];
Can anyone suggest me some way or method to resolve this issue. By doing google search I found that in UITextView there is contentSize method which can get size of its content, I tried to find similar method in NSTextView but could not get any success :(
Based on #Peter Hosey's answer, here is an extension to NSTextView in Swift 4.2:
extension NSTextView {
var contentSize: CGSize {
get {
guard let layoutManager = layoutManager, let textContainer = textContainer else {
print("textView no layoutManager or textContainer")
return .zero
}
layoutManager.ensureLayout(for: textContainer)
return layoutManager.usedRect(for: textContainer).size
}
}
}
NSTextView *textView = [[NSTextView alloc] init];
textView.font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
textView.string = #"Lorem ipsum";
[textView.layoutManager ensureLayoutForTextContainer:textView.textContainer];
textView.frame = [textView.layoutManager usedRectForTextContainer:textView.textContainer];
+ (float)heightForString:(NSString *)myString font:(NSFont *)myFont andWidth:(float)myWidth andPadding:(float)padding {
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:myString];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth, FLT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
[textStorage addAttribute:NSFontAttributeName value:myFont
range:NSMakeRange(0, textStorage.length)];
textContainer.lineFragmentPadding = padding;
(void) [layoutManager glyphRangeForTextContainer:textContainer];
return [layoutManager usedRectForTextContainer:textContainer].size.height;
}
I did the function using this reference: Documentation
Example:
float width = textView.frame.size.width - 2 * textView.textContainerInset.width;
float proposedHeight = [Utils heightForString:textView.string font:textView.font andWidth:width
andPadding:textView.textContainer.lineFragmentPadding];

Resources