我的应用程序使用UITextView。现在我想让UITextView有一个占位符,类似于你可以为UITextField设置的占位符。
如何做到这一点?
我的应用程序使用UITextView。现在我想让UITextView有一个占位符,类似于你可以为UITextField设置的占位符。
如何做到这一点?
当前回答
另一个解决方案
import UIKit
protocol PlaceholderTextViewDelegate: class {
func placeholderTextViewDidChangeText(_ text: String)
func placeholderTextViewDidEndEditing(_ text: String)
}
final class PlaceholderTextView: UITextView {
weak var notifier: PlaceholderTextViewDelegate?
var ignoreEnterAction: Bool = true
var placeholder: String? {
didSet {
text = placeholder
selectedRange = NSRange(location: 0, length: 0)
}
}
var placeholderColor = UIColor.lightGray {
didSet {
if text == placeholder {
textColor = placeholderColor
}
}
}
var normalTextColor = UIColor.lightGray
var placeholderFont = UIFont.sfProRegular(28)
fileprivate var placeholderLabel: UILabel?
// MARK: - LifeCycle
override var text: String? {
didSet {
if text == placeholder {
textColor = placeholderColor
} else {
textColor = normalTextColor
}
}
}
init() {
super.init(frame: CGRect.zero, textContainer: nil)
awakeFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.delegate = self
}
}
extension PlaceholderTextView: UITextViewDelegate {
// MARK: - UITextViewDelegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "" && textView.text == placeholder {
return false
}
if let placeholder = placeholder,
textView.text == placeholder,
range.location <= placeholder.count {
textView.text = ""
}
if ignoreEnterAction && text == "\n" {
textView.resignFirstResponder()
return false
}
return true
}
func textViewDidChange(_ textView: UITextView) {
if let placeholder = placeholder {
textView.text = textView.text.replacingOccurrences(of: placeholder, with: "")
}
if let placeholder = placeholder,
text?.isEmpty == true {
text = placeholder
textColor = placeholderColor
selectedRange = NSRange(location: 0, length: 0)
} else {
textColor = normalTextColor
}
notifier?.placeholderTextViewDidChangeText(textView.text)
}
func textViewDidChangeSelection(_ textView: UITextView) {
if let placeholder = placeholder,
text == placeholder {
selectedRange = NSRange(location: 0, length: 0)
}
}
func textViewDidEndEditing(_ textView: UITextView) {
notifier?.placeholderTextViewDidEndEditing(textView.text)
if let placeholder = placeholder,
text?.isEmpty == true {
text = placeholder
textColor = placeholderColor
selectedRange = NSRange(location: 0, length: 0)
} else {
textColor = normalTextColor
}
}
}
结果:
其他回答
看一下UTPlaceholderTextView。
这是UITextView的一个方便的子类,它支持类似于UITextField的占位符。主要特点:
不使用子视图 不重写drawRect: 占位符的长度可以是任意的,并以与通常文本相同的方式呈现
我已经创建了一个快速3版本的最高排名的答案
你只需要做UITextView的子类化。
import UIKit
class UIPlaceHolderTextView: UITextView {
//MARK: - Properties
@IBInspectable var placeholder: String?
@IBInspectable var placeholderColor: UIColor?
var placeholderLabel: UILabel?
//MARK: - Initializers
override func awakeFromNib() {
super.awakeFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Use Interface Builder User Defined Runtime Attributes to set
// placeholder and placeholderColor in Interface Builder.
if self.placeholder == nil {
self.placeholder = ""
}
if self.placeholderColor == nil {
self.placeholderColor = UIColor.black
}
NotificationCenter.default.addObserver(self, selector: #selector(textChanged(_:)), name: NSNotification.Name.UITextViewTextDidChange, object: nil)
}
func textChanged(_ notification: Notification) -> Void {
if self.placeholder?.count == 0 {
return
}
UIView.animate(withDuration: 0.25) {
if self.text.count == 0 {
self.viewWithTag(999)?.alpha = 1
}
else {
self.viewWithTag(999)?.alpha = 0
}
}
}
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
super.draw(rect)
if (self.placeholder?.count ?? 0) > 0 {
if placeholderLabel == nil {
placeholderLabel = UILabel.init()
placeholderLabel?.lineBreakMode = .byWordWrapping
placeholderLabel?.numberOfLines = 0
placeholderLabel?.font = self.font
placeholderLabel?.backgroundColor = self.backgroundColor
placeholderLabel?.textColor = self.placeholderColor
placeholderLabel?.alpha = 0
placeholderLabel?.tag = 999
self.addSubview(placeholderLabel!)
placeholderLabel?.translatesAutoresizingMaskIntoConstraints = false
placeholderLabel?.topAnchor.constraint(equalTo: self.topAnchor, constant: 7).isActive = true
placeholderLabel?.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 4).isActive = true
placeholderLabel?.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
placeholderLabel?.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
}
placeholderLabel?.text = self.placeholder
placeholderLabel?.sizeToFit()
self.sendSubview(toBack: self.placeholderLabel!)
}
if self.text.count == 0 && (self.placeholder?.count ?? 0) > 0 {
self.viewWithTag(999)?.alpha = 1
}
}
}
我对bcd的解决方案做了一些小修改,以允许从Xib文件初始化、文本包装和保持背景颜色。希望它能为其他人省去麻烦。
UIPlaceHolderTextView.h:
#import <Foundation/Foundation.h>
IB_DESIGNABLE
@interface UIPlaceHolderTextView : UITextView
@property (nonatomic, retain) IBInspectable NSString *placeholder;
@property (nonatomic, retain) IBInspectable UIColor *placeholderColor;
-(void)textChanged:(NSNotification*)notification;
@end
UIPlaceHolderTextView.m:
#import "UIPlaceHolderTextView.h"
@interface UIPlaceHolderTextView ()
@property (nonatomic, retain) UILabel *placeHolderLabel;
@end
@implementation UIPlaceHolderTextView
CGFloat const UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25;
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
#if __has_feature(objc_arc)
#else
[_placeHolderLabel release]; _placeHolderLabel = nil;
[_placeholderColor release]; _placeholderColor = nil;
[_placeholder release]; _placeholder = nil;
[super dealloc];
#endif
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Use Interface Builder User Defined Runtime Attributes to set
// placeholder and placeholderColor in Interface Builder.
if (!self.placeholder) {
[self setPlaceholder:@""];
}
if (!self.placeholderColor) {
[self setPlaceholderColor:[UIColor lightGrayColor]];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
- (id)initWithFrame:(CGRect)frame
{
if( (self = [super initWithFrame:frame]) )
{
[self setPlaceholder:@""];
[self setPlaceholderColor:[UIColor lightGrayColor]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
return self;
}
- (void)textChanged:(NSNotification *)notification
{
if([[self placeholder] length] == 0)
{
return;
}
[UIView animateWithDuration:UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION animations:^{
if([[self text] length] == 0)
{
[[self viewWithTag:999] setAlpha:1];
}
else
{
[[self viewWithTag:999] setAlpha:0];
}
}];
}
- (void)setText:(NSString *)text {
[super setText:text];
[self textChanged:nil];
}
- (void)drawRect:(CGRect)rect
{
if( [[self placeholder] length] > 0 )
{
if (_placeHolderLabel == nil )
{
_placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8,8,self.bounds.size.width - 16,0)];
_placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping;
_placeHolderLabel.numberOfLines = 0;
_placeHolderLabel.font = self.font;
_placeHolderLabel.backgroundColor = [UIColor clearColor];
_placeHolderLabel.textColor = self.placeholderColor;
_placeHolderLabel.alpha = 0;
_placeHolderLabel.tag = 999;
[self addSubview:_placeHolderLabel];
}
_placeHolderLabel.text = self.placeholder;
[_placeHolderLabel sizeToFit];
[self sendSubviewToBack:_placeHolderLabel];
}
if( [[self text] length] == 0 && [[self placeholder] length] > 0 )
{
[[self viewWithTag:999] setAlpha:1];
}
[super drawRect:rect];
}
@end
我扩展了KmKndy的答案,这样在用户开始编辑UITextView之前,占位符仍然是可见的,而不仅仅是点击它。这反映了Twitter和Facebook应用程序的功能。我的解决方案不需要你子类和工作,如果用户类型直接或粘贴文本!
- (void)textViewDidChangeSelection:(UITextView *)textView{
if ([textView.text isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]])[textView setSelectedRange:NSMakeRange(0, 0)];
}
- (void)textViewDidBeginEditing:(UITextView *)textView{
[textView setSelectedRange:NSMakeRange(0, 0)];
}
- (void)textViewDidChange:(UITextView *)textView
{
if (textView.text.length != 0 && [[textView.text substringFromIndex:1] isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]]){
textView.text = [textView.text substringToIndex:1];
textView.textColor = [UIColor blackColor]; //optional
}
else if(textView.text.length == 0){
textView.text = @"What's happening?";
textView.textColor = [UIColor lightGrayColor];
[textView setSelectedRange:NSMakeRange(0, 0)];
}
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
if ([textView.text isEqualToString:@""]) {
textView.text = @"What's happening?";
textView.textColor = [UIColor lightGrayColor]; //optional
}
[textView resignFirstResponder];
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
if (textView.text.length > 1 && [textView.text isEqualToString:@"What's happening?"]) {
textView.text = @"";
textView.textColor = [UIColor blackColor];
}
return YES;
}
只需要记住在创建时使用准确的文本设置myUITextView即可。
UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"What's happening?";
myUITextView.textColor = [UIColor lightGrayColor]; //optional
在包含这些方法之前,让父类成为一个UITextView委托。
@interface MyClass () <UITextViewDelegate>
@end
还有一个简单的答案,使用CATextLayer。
添加CATextLayer到UITextView的层。 使用UITextViewDelegate方法,简单地改变CATextLayer的颜色。
func txtViewPlaceholder() {
let textlayer = CATextLayer()
textlayer.frame = CGRect(x: 5, y: 5, width: 200, height: 18)
textlayer.contentsScale = UIScreen.main.scale
textlayer.fontSize = 12
textlayer.alignmentMode = kCAAlignmentLeft
textlayer.string = "Enter here"
textlayer.isWrapped = true
textlayer.name = "placeholder"
textlayer.backgroundColor = UIColor.white.cgColor
textlayer.foregroundColor = UIColor.black.cgColor
yourTxtVw.layer.insertSublayer(textlayer, at: 0)
}
func removeAddPlaceholder(remove: Bool, textView: UITextView) {
for layers in textView.layer.sublayers! where layers.name == "placeholder" {
if remove {
(layers as! CATextLayer).foregroundColor = UIColor.white.cgColor
} else {
(layers as! CATextLayer).foregroundColor = UIColor.black.cgColor
}
}
}
extension YourViewController : UITextViewDelegate {
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
removeAddPlaceholder(remove: true, textView: textView)
return true
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.count <= 0 {
removeAddPlaceholder(remove: false, textView: textView)
}
}
}