iOS 13 - 带有占位符的 UITextField 让应用程序崩溃

Posted

技术标签:

【中文标题】iOS 13 - 带有占位符的 UITextField 让应用程序崩溃【英文标题】:iOS 13 - UITextField with Placeholder getting app crash 【发布时间】:2019-10-24 21:18:54 【问题描述】:

ios 13 中,我在访问 UITextField _placeholderLabel.textColor 标签键时遇到了崩溃。

用于应用占位符文本颜色的键。

[textfield setValue:[UIColor whiteColor] forKeyPath:@"_placeholderLabel.textColor"];

“NSGenericException” - 原因:“禁止访问 UITextField 的 _placeholderLabel ivar。这是一个应用程序错误”

【问题讨论】:

这个问题也存在于 Xcode 11 GM 种子中。 @SunilTarge 检查答案 - ***.com/a/56776561/3560390 这不是“问题”。不允许戳穿内部实现细节。您违反了 App Store 准则。您尝试的任何“解决方法”都将很快再次被打破。停止。 @PrasathBabu 这个冻结只适用于模拟器,对吗?只要在真实设备中没有冻结那么这不是问题吗?我是初学者。我很困惑,因为我在真实设备上没有这种冻结,而只是在模拟器中。所以如果我不关心模拟器,我不需要在下面添加答案吗?请真的需要你的信息 @Alexa289 设备上发生崩溃 - 当您使用 Xcode11 和 iOS 13 设备构建应用程序时。当应用在 Xcode11 之前构建并安装在 iOS 13 设备中时,不会发生崩溃。 【参考方案1】:

您可以使用运行时来做到这一点:

将以下代码添加到占位符设置的底部

Ivar ivar =  class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(textField, ivar);
placeholderLabel.textColor = [UIColor whiteColor];

在Xcode 11 beta2,这个代码是可以工作的,但我不知道GM版还是正式版。

完整代码:

Objective-C 版本

#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 版本:
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 更新

上述实现可以解决问题,但不提倡。

使用私有 api 的应用将来可能会损坏。

请使用新的api:

var attributedPlaceholder: NSAttributedString?  get set 

讨论

此属性默认为 nil。如果设置,则使用系统定义的颜色和属性字符串的剩余样式信息(文本颜色除外)绘制占位符字符串。为该属性分配一个新值也会用相同的字符串数据替换占位符属性的值,尽管没有任何格式信息。为该属性分配新值不会影响文本字段的任何其他与样式相关的属性。

完整代码:

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)

【讨论】:

谢谢。这个“错误”在 Beta 6 中仍然存在。用 Swift 编写并在那里工作。 为什么你会建议人们做一些显然不允许的事情,通过发布另一个解决方法?这不行,别再这样了。您的应用将来会损坏。 @russbishop 任何解决方案如何改变它呢?谢谢 @russbishop 谢谢你提醒我。我已经更新了我的答案。 @iDevOrz 清楚地写在文档中“如果设置,则使用系统定义的颜色和剩余的样式信息(文本颜色除外)绘制占位符字符串”。所以你没有在更新中回答问题【参考方案2】:

你可以设置UITextField占位文字颜色,字体使用attributedPlaceholder方法如下:

NSString *text = [textField text];
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:text attributes:@NSForegroundColorAttributeName: [UIColor redColor], NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Medium" size:14.0f]];

【讨论】:

【参考方案3】:

我是这样做的:

对象

NSMutableAttributedString *placeholderAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:searchField.attributedPlaceholder];
[placeholderAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [placeholderAttributedString length])];
searchField.attributedPlaceholder = placeholderAttributedString;

斯威夫特 5

var placeholderAttributedString = NSMutableAttributedString(attributedString: searchField.attributedPlaceholder)
    placeholderAttributedString.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 0, length: placeholderAttributedString.length))
    searchField.attributedPlaceholder = placeholderAttributedString

有点长,但我暂时没有更好的了。

【讨论】:

目前只能接受的答案,我使用的是 Xcode 11.0 尝试擦除模拟器上的所有内容和设置:)【参考方案4】:

- 斯威夫特版本

将以下代码添加到占位符设置的底部:

    let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
    let placeholderLabel = object_getIvar(textField, iVar) as! UILabel

    placeholderLabel.textColor = .white

例如:

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

【讨论】:

【参考方案5】:

“删除所有内容和设置”

这对我有用。

【讨论】:

【参考方案6】:

斯威夫特 5

这是该属性的正确 Swift 替换。 请注意,如果您还更改了文本字段的其他属性的对齐方式,那么您也需要将这些属性设置为占位符的属性文本。通常只改变颜色和字体:

/// 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)

【讨论】:

【参考方案7】:

如果您使用的是 UITextView+Placeholder.h 类,请更改以下方法 Clean and build 如果问题仍然存在

目标 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;


【讨论】:

【参考方案8】:

用于 UISEARCHBAR 占位符 颜色

Xcode 11.1

从 iOS 13 开始,SDK 提供了 UISearchBar.searchTextField,所以我们可以使用 公共 API 而不是私有 API。在 UISearchBar 的子类中,我使用此代码更改占位符颜色

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"];


【讨论】:

【参考方案9】:

对于 IOS 13,您可以通过一行代码设置 UITextField 占位符颜色。

目标 C

[textField setAttributedPlaceholder:[[NSAttributedString alloc] initWithString:@"PlaceHolder Text" attributes:@NSForegroundColorAttributeName:[UIColor whiteColor]]];

Swift 5

txtTitle.attributedPlaceholder = NSAttributedString(string:"PlaceHolder Text", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])

【讨论】:

【参考方案10】:

我认为这是 XCode 方面的一个错误,希望他们会在下一个版本中修复这个错误。您可以通过擦除模拟器设备上的所有内容和设置来快速解决此问题。

【讨论】:

【参考方案11】:

我删除了“用户定义的运行时属性”部分中的 var “_placeholderLabel”

【讨论】:

【参考方案12】:

删除下划线“_”并试试这个。

[textfield setValue:[UIColor whiteColor] forKeyPath:@"placeholderLabel.textColor"];

它应该适用于所有版本!

【讨论】:

这在模拟器和 Xcode 11.2.1 中对我有用。谢谢【参考方案13】:

是的,这个问题与 Xcode 版本有关。我有 Xcode 11.2.1,我面临同样的问题。

_placeholderLabel.textColor

如果您从情节提要中使用它作为运行时变量并遇到问题,那么只需删除“_”

这样

删除“_”运行时 Var 后开始工作

【讨论】:

【参考方案14】:

只需将_placeholderLabel 替换为placeholderLabel

【讨论】:

【参考方案15】:

从“_placeholderLabel.textColor”中删除“_”试试“placeholderLabel.textColor”。

【讨论】:

以上是关于iOS 13 - 带有占位符的 UITextField 让应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

带有占位符的 FormatMessage

带有模板占位符的 Url Helper 路由

Laravel 数据库选择带有占位符的下拉菜单

带有可选占位符的 string.format()

带有位置占位符的 ON DUPLICATE KEY UPDATE 的语法

IE10+ 上的 AngularJS,带有占位符的文本区域导致“无效参数”。