如何创建一个带有占位符、多行和垂直居中的UITextView或UITextField?

3
我想要的是一个非常直观的东西,但令人惊讶的是它没有包括在Xcode中: 我想要的是一个textView或textField,它将有:
1-一个占位符 2-几行(带有像UITextView一样的滚动视图)。 3-文本或占位符应始终在textBoxArea中垂直和水平居中。
我看过类似的问题,如: 在UITextView中垂直居中文本 在UITextView中垂直和水平居中文本 在UITextView中垂直居中对齐文本 对于占位符,我正在使用以下库: https://github.com/devxoul/UITextView-Placeholder 它解决了第一个要求,但即使使用上面链接中给出的答案,我也无法满足其他两个要求。
我正在使用Swift 3.1,部署目标为iOS9,Xcode 8.3.2。

UITextView已经支持多行文本输入。UITextField意图只有单行输入空间。如果你需要多行输入,可以使用UITextView代替。 - PGDev
@PGDev 我知道,但UITextView的文本从视图顶部开始而不是中间...并且没有占位符选项..... - Pomanh
关于TextView中的占位符,您可以参考以下链接:https://dev59.com/dnM_5IYBdhLWcg3wjj4p#10201671 - PGDev
@PGDev 我已经有了解决方案,在问题正文中...库的放置占位符很好用...最大的问题是将占位符和文本垂直和水平居中。 - Pomanh
2个回答

1
很遗憾,UITextViewUITextField都非常有限。这些事情已经被许多开发人员做过很多次了。
我通常做的就是创建一个新的UIView子类,然后有两个子视图;一个用于占位符的标签,另一个用于内容的居中文本视图。
你可以在xib中完成并使用界面构建器,我建议你这样做。然后你可以公开文本视图和占位符,并且你可能会设置几乎所有你需要的东西。
但是如果要在代码中实现它,看起来会像以下内容:
@IBDesignable class CustomTextView: UIView {

    private(set) var textView: UITextView = UITextView(frame: CGRect.zero)
    private(set) var placeholderLabel: UILabel = UILabel(frame: CGRect.zero)

    @IBInspectable var placeholder: String? {
        didSet {
            placeholderLabel.text = placeholder
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    private func setup() {
        textView.delegate = self
        addSubview(textView)
        textView.backgroundColor = UIColor.clear
        textView.textAlignment = .center

        addSubview(placeholderLabel)
        placeholderLabel.textAlignment = .center
        placeholderLabel.numberOfLines = 0

        addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        refreshViews()
    }

    fileprivate func refreshViews() {
        UIView.performWithoutAnimation {
            textView.frame = self.bounds

            let optimalSize = textView.sizeThatFits(bounds.size)
            textView.frame = CGRect(x: 0.0, y: max((bounds.size.height-optimalSize.height)*0.5, 0.0), width: bounds.width, height: min(optimalSize.height, bounds.size.height))
            placeholderLabel.frame = bounds
            placeholderLabel.backgroundColor = UIColor.clear

            if textView.text.isEmpty && textView.isFirstResponder == false {
                placeholderLabel.text = placeholder
                placeholderLabel.isHidden = false
                textView.isHidden = true
            } else {
                placeholderLabel.isHidden = true
                textView.isHidden = false
            }
        }
    }

    @objc private func onTap() {
        if !textView.isFirstResponder {
            textView.becomeFirstResponder()
        }
    }

}

extension CustomTextView: UITextViewDelegate {

    func textViewDidChange(_ textView: UITextView) {
        refreshViews()
    }

    func textViewDidBeginEditing(_ textView: UITextView) {
        refreshViews()
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        refreshViews()
    }

}

然而,仍有一些缺点:
  1. 文本视图的委托由视图本身需要。因此,如果您在某个视图控制器中调用 myView.textView.delegate = self,则会破坏内部逻辑。如果需要,建议在此视图上创建自定义委托并公开所需的方法,而不是使用标准的 UITextViewDelegate
  2. 尽管该类可设计,但无法拥有许多可检查字段。因此,在界面生成器中设置任何属性有点困难。您可以公开诸如“text”之类的属性,并将setter和getter指向文本视图。对于文本颜色也可以这样做...但最大的问题是无法将字体标记为可检查(向苹果哭泣吧)。
  3. 在某些情况下,换行时文本可能会跳动一点。我想您需要稍微调整一下才能完美解决。可能是约束条件可以解决此问题,因此如果在xib中制作它,则可以解决此问题。
好处是您可以从代码中做任何事情。多行或带属性的字符串占位符、对齐、颜色、字体...

我现在会尝试并告诉你。 - Pomanh
谢谢@Matic Oblak,但我用另一种方法解决了这个问题...不知怎么的,我忘记了委托...但是你的回答让我想起了它们。 - Pomanh

0
其实我感觉有点傻,因为答案一直在我的眼前...
对于占位符,我将使用库:https://github.com/devxoul/UITextView-Placeholder,这也是我在问题中提到的。
然后在UIViewController类中,我做了以下操作:
class myClass: UIViewController {

      @IBOutlet weak var myTextView: UITextView!

      override func viewDidLoad() {
          super.viewDidLoad()
          // Set the textView to the Delegate 
          myTextView.delegate = self
          // Initialize the textView text Position and accordingly the
          //   placeholder position 
          myTextView.contentInset.top = max(0, (tv_comment.bounds.size.height - tv_comment.contentSize.height * tv_comment.zoomScale) / 2)

      }

       extension myClass: UITextViewDelegate {
              func textViewDidChange(_ textView: UITextView) {
                   // do the same adjustment again every time the textView changes
                   textView.contentInset.top = max(0, (textView.bounds.size.height - textView.contentSize.height * textView.zoomScale) / 2)
              }
       }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接