我的应用程序使用UITextView。现在我想让UITextView有一个占位符,类似于你可以为UITextField设置的占位符。
如何做到这一点?
我的应用程序使用UITextView。现在我想让UITextView有一个占位符,类似于你可以为UITextField设置的占位符。
如何做到这一点?
当前回答
一个更简单的方法是创建一个次要的UITextView,它具有与原始文本视图相同的所有属性,除了不同的textColor,并带有约束以确保它们保持对齐。然后当任何字符进入主文本视图时,隐藏克隆文本视图,否则显示带有一些文本的克隆文本视图。
这可以通过几种方式实现,但相对简单的方法是子类化UITextView并将所有这些逻辑保留在子类中。
所以,子类UITextView并允许它惰性地创建它的占位符视图
接口文件:
@interface FOOTextView : UITextView <UITextViewDelegate>
@property (nonatomic, copy) NSString *placeholderText;
- (void)checkPlaceholder;
@end
实现文件:
#import "FOOTextView.h"
@interface FOOTextView ()
@property (nonatomic, strong) UITextView *placeholderTextView;
@end
@implementation FOOTextView
- (void)checkPlaceholder {
// Hide the placeholder text view if we've got any text
self.placeholderTextView.hidden = (self.text.length > 0 || self.attributedText.length > 0);
}
- (void)setPlaceholderText:(NSString *)placeholderText {
_placeholderText = [placeholderText copy];
// Setup the placeholder text view if we haven't already
[self setupPlaceholderTextView];
// Apply the placeholder text to the placeholder text view
self.placeholderTextView.text = placeholderText;
}
- (void)setupPlaceholderTextView {
if (!self.placeholderTextView) {
// Setup the place holder text view, duplicating our visual setup
self.placeholderTextView = [[UITextView alloc] initWithFrame:CGRectZero];
self.placeholderTextView.translatesAutoresizingMaskIntoConstraints = NO;
self.placeholderTextView.textColor = self.placeholderTextColor ? self.placeholderTextColor : [UIColor colorWithRed:199.f/255.f green:199.f/255.f blue:205.f/255.f alpha:1.f];
self.placeholderTextView.userInteractionEnabled = NO;
self.placeholderTextView.font = self.font;
self.placeholderTextView.textAlignment = self.textAlignment;
self.placeholderTextView.backgroundColor = self.backgroundColor;
self.placeholderTextView.editable = NO;
// Our background color must be clear for the placeholder text view to show through
self.backgroundColor = [UIColor clearColor];
// Insert the placeholder text view into our superview, below ourself so it shows through
[self.superview insertSubview:self.placeholderTextView belowSubview:self];
// Setup constraints to ensure the placeholder text view stays aligned with us
NSLayoutConstraint *constraintCenterX = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f];
NSLayoutConstraint *constraintCenterY = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:0.f];
NSLayoutConstraint *constraintWidth = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.f constant:0.f];
NSLayoutConstraint *constraintHeight = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f];
NSArray *constraints = @[constraintCenterX, constraintCenterY, constraintWidth, constraintHeight];
[self.superview addConstraints:constraints];
}
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor {
_placeholderTextColor = placeholderTextColor;
self.placeholderTextView.textColor = _placeholderTextColor;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor {
// We don't want a background color ourselves, instead we want our placeholder text view to have the desired background color
[self.placeholderTextView setBackgroundColor:backgroundColor];
}
- (void)removeFromSuperview {
// Ensure we also remove our placeholder text view
[self.placeholderTextView removeFromSuperview];
self.placeholderTextView = nil;
[super removeFromSuperview];
}
#pragma mark - Text View Delegation
- (void)textViewDidChange:(UITextView *)textView {
[self checkPlaceholder];
}
@end
使用上面的类,如果你设置了一个FOOTextView的委托实例到它自己,一切都将开箱工作:
FOOTextView *myTextView = ...
myTextView.placeholderText = @"What's on your mind?";
myTextView.placeholderTextColor = [UIColor lightGrayColor];
myTextView.delegate = myTextView;
如果你想另一个对象接管作为委托,那么你只需要在textViewDidChange: delegate方法中调用文本视图的checkPlaceholder方法,例如;
FOOTextView *myTextView = ...
myTextView.placeholderText = @"What's on your mind?";
myTextView.placeholderTextColor = [UIColor lightGrayColor];
myTextView.delegate = self;
self.myTextView = myTextView;
- (void)textViewDidChange:(UITextView *)textView {
// Call the checkPlaceholder method to update the visuals
[self.myTextView checkPlaceholder];
}
其他回答
更简单,并且仍然考虑到用户在某些时候输入的一些文本
BOOL placeHolderTextVisible;
在viewDidLoad上,设置它为YES(或DidMoveToSuperview,或awakeFromNib)
然后,在 - (BOOL) textView:(UITextView*)textView shouldBeginEditing
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
{
if (placeHolderTextVisible) {
placeHolderTextVisible = NO;
textView.text = @"";
}
return YES;
}
我建议使用SZTextView。
https://github.com/glaszig/SZTextView
从storyboard中添加默认的UITextView,然后将其自定义类更改为SZTextView,如下所示
然后您将在属性检查器中看到两个新选项
下面是“SAMTextView”ObjC代码的Swift端口,作为对这个问题的第一批回答之一。我在iOS 8上进行了测试。我调整了一些东西,包括占位符文本的位置的边界偏移,因为原来的位置太高,太右了(在那个帖子的一个评论中使用了建议)。
我知道有很多简单的解决方案,但我喜欢子类化UITextView的方法,因为它是可重用的,而且我不必在使用它的机制时使类变得混乱。
斯威夫特2.2:
import UIKit
class PlaceholderTextView: UITextView {
@IBInspectable var placeholderColor: UIColor = UIColor.lightGrayColor()
@IBInspectable var placeholderText: String = ""
override var font: UIFont? {
didSet {
setNeedsDisplay()
}
}
override var contentInset: UIEdgeInsets {
didSet {
setNeedsDisplay()
}
}
override var textAlignment: NSTextAlignment {
didSet {
setNeedsDisplay()
}
}
override var text: String? {
didSet {
setNeedsDisplay()
}
}
override var attributedText: NSAttributedString? {
didSet {
setNeedsDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
}
private func setUp() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PlaceholderTextView.textChanged(_:)),
name: UITextViewTextDidChangeNotification, object: self)
}
func textChanged(notification: NSNotification) {
setNeedsDisplay()
}
func placeholderRectForBounds(bounds: CGRect) -> CGRect {
var x = contentInset.left + 4.0
var y = contentInset.top + 9.0
let w = frame.size.width - contentInset.left - contentInset.right - 16.0
let h = frame.size.height - contentInset.top - contentInset.bottom - 16.0
if let style = self.typingAttributes[NSParagraphStyleAttributeName] as? NSParagraphStyle {
x += style.headIndent
y += style.firstLineHeadIndent
}
return CGRect(x: x, y: y, width: w, height: h)
}
override func drawRect(rect: CGRect) {
if text!.isEmpty && !placeholderText.isEmpty {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = textAlignment
let attributes: [ String: AnyObject ] = [
NSFontAttributeName : font!,
NSForegroundColorAttributeName : placeholderColor,
NSParagraphStyleAttributeName : paragraphStyle]
placeholderText.drawInRect(placeholderRectForBounds(bounds), withAttributes: attributes)
}
super.drawRect(rect)
}
}
斯威夫特4.2:
import UIKit
class PlaceholderTextView: UITextView {
@IBInspectable var placeholderColor: UIColor = UIColor.lightGray
@IBInspectable var placeholderText: String = ""
override var font: UIFont? {
didSet {
setNeedsDisplay()
}
}
override var contentInset: UIEdgeInsets {
didSet {
setNeedsDisplay()
}
}
override var textAlignment: NSTextAlignment {
didSet {
setNeedsDisplay()
}
}
override var text: String? {
didSet {
setNeedsDisplay()
}
}
override var attributedText: NSAttributedString? {
didSet {
setNeedsDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
}
private func setUp() {
NotificationCenter.default.addObserver(self,
selector: #selector(self.textChanged(notification:)),
name: Notification.Name("UITextViewTextDidChangeNotification"),
object: nil)
}
@objc func textChanged(notification: NSNotification) {
setNeedsDisplay()
}
func placeholderRectForBounds(bounds: CGRect) -> CGRect {
var x = contentInset.left + 4.0
var y = contentInset.top + 9.0
let w = frame.size.width - contentInset.left - contentInset.right - 16.0
let h = frame.size.height - contentInset.top - contentInset.bottom - 16.0
if let style = self.typingAttributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle {
x += style.headIndent
y += style.firstLineHeadIndent
}
return CGRect(x: x, y: y, width: w, height: h)
}
override func draw(_ rect: CGRect) {
if text!.isEmpty && !placeholderText.isEmpty {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = textAlignment
let attributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue) : font!,
NSAttributedString.Key(rawValue: NSAttributedString.Key.foregroundColor.rawValue) : placeholderColor,
NSAttributedString.Key(rawValue: NSAttributedString.Key.paragraphStyle.rawValue) : paragraphStyle]
placeholderText.draw(in: placeholderRectForBounds(bounds: bounds), withAttributes: attributes)
}
super.draw(rect)
}
}
这是我的版本的UITextView与占位符支持。斯威夫特4.2 https://gist.github.com/hlung/c5dda3a0c2087e5ae6c1fce8822c4713
一个支持占位符文本的UITextView子类。它使用另一个 UILabel显示占位符,当文本为空时显示。
这完美地模仿了UITextField的占位符,其中占位符文本一直保留到您实际键入一些内容为止。
private let placeholder = "Type here"
@IBOutlet weak var textView: UITextView! {
didSet {
textView.textColor = UIColor.lightGray
textView.text = placeholder
textView.selectedRange = NSRange(location: 0, length: 0)
}
}
extension ViewController: UITextViewDelegate {
func textViewDidChangeSelection(_ textView: UITextView) {
// Move cursor to beginning on first tap
if textView.text == placeholder {
textView.selectedRange = NSRange(location: 0, length: 0)
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if textView.text == placeholder && !text.isEmpty {
textView.text = nil
textView.textColor = UIColor.black
textView.selectedRange = NSRange(location: 0, length: 0)
}
return true
}
func textViewDidChange(_ textView: UITextView) {
if textView.text.isEmpty {
textView.textColor = UIColor.lightGray
textView.text = placeholder
}
}
}