简体   繁体   English

如何创建一个具有placeHolder,多行和垂直居中的UITextView或UITextField?

[英]How to create a UITextView or a UITextField that has a placeHolder, multiline and vertically centered?

What I want is a very intuitive thing but surprisingly it is not included in Xcode: what I want is a textView or a textField that will have 我想要的是一个非常直观的东西,但是令人惊讶的是它没有包含在Xcode中:我想要的是一个textView或textField,它将具有

1- a placeholder 1-占位符
2- several lines (with scroll view like in UITextView). 2-几行(具有类似UITextView的滚动视图)。
3- either the text or the placeholder should always be vertically and horizontally centered in the textBoxArea . 3-文本或占位符应始终在textBoxArea中垂直和水平居中。

I've looked at similar questions like: 我看过类似的问题,例如:
Center text vertically in a UITextView 在UITextView中垂直居中放置文本
Center the text in a UITextView, vertically and horizontally 将文本在UITextView中垂直和水平居中
Aligning text vertically center in UITextView 在UITextView中垂直居中对齐文本

for the placeHolder I am using the following library: 对于placeHolder,我正在使用以下库:
https://github.com/devxoul/UITextView-Placeholder which solves the first requirement but I couldn't satisfy the other 2 requirements even with the answer given in the links above https://github.com/devxoul/UITextView-Placeholder解决了第一个要求,但是即使上面的链接给出了答案,我也无法满足其他两个要求

I am using swift 3.1, deployment target iOS9, Xcode 8.3.2 我正在使用swift 3.1,部署目标iOS9,Xcode 8.3.2

It is unfortunate that both UITextView and UITextField are so limited. 不幸的是, UITextViewUITextField是如此有限。 These same things have been done for so many times by so many developers. 许多开发人员已经做过很多次相同的事情。

What I usually do is just create a new UIView subclass which then has 2 subviews; 我通常要做的就是创建一个新的UIView子类,然后该子类具有2个子视图。 A label for placeholder and centered text view for content. 占位符的标签和内容的居中文本视图。

You can do it in xib and use the interface builder and I suggest you to do so. 您可以在xib中进行操作,并使用界面构建器,建议您这样做。 You can then expose both text view and placeholder and you may actually set pretty much anything you would ever need. 然后,您可以同时显示文本视图和占位符,并且实际上可以设置几乎所需的任何内容。

But to do it in the code it would look something like the following: 但是要在代码中执行此操作,将类似于以下内容:

@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()
    }

}

There are still a few downsides though: 但是仍然存在一些缺点:

  1. The delegate for the text view is needed by the view itself. 文本视图的委托是视图本身所需的。 So if you would call myView.textView.delegate = self in some view controller you would break the logic inside. 因此,如果您在某些视图控制器中调用myView.textView.delegate = self ,则会破坏内部的逻辑。 If you need it I suggest you create a custom delegate on this view and expose the methods you need instead of the standard UITextViewDelegate . 如果需要,建议您在此视图上创建一个自定义委托,并公开所需的方法,而不是标准的UITextViewDelegate
  2. Although the class is designable you can not have many inspectable fields. 尽管该类是可设计的,但是您不能有许多可检查的字段。 So it is a bit hard to set any properties inside the interface builder. 因此,很难在界面生成器中设置任何属性。 You could expose properties such as "text" and put setter and getter to point to the text view. 您可以公开诸如“文本”之类的属性,并将setter和getter指向文本视图。 Could do the same for text color... But the biggest problem is you cant mark fonts as inspectable (cry to Apple I guess). 可以为文本颜色做同样的事情……但是最大的问题是您不能将字体标记为可检查的(我想是对苹果的哭泣)。
  3. In some cases text jumps a bit when going into newline. 在某些情况下,进入换行符时文本会跳一些。 I guess you would need to play with it a bit to perfect it. 我想您需要花一点时间来完善它。 It might be that constraints would fix this so if you make it in xib this could solve it. 可能是约束条件可以解决此问题,因此,如果您在xib中完成此操作,则可以解决该问题。

As a good thing though you can do pretty much anything from the code. 幸运的是,您可以从代码中执行几乎所有操作。 Multiline or attributed string placeholders, alignments, colors, fonts... 多行或属性字符串占位符,对齐方式,颜色,字体...

actually I feel a little silly because the answer was infront of my eye all the time ... 实际上我有点傻,因为答案一直都在我的眼前。

For the PlaceHolder I'll just use the library: https://github.com/devxoul/UITextView-Placeholder mentioned in my question 对于PlaceHolder,我将仅使用该库:我的问题中提到的https://github.com/devxoul/UITextView-Placeholder

then in the UIViewController class I did the following: 然后在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)
              }
       }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM