以编程方式创建输入附件视图

Posted

技术标签:

【中文标题】以编程方式创建输入附件视图【英文标题】:Creating an Input Accessory View programmatically 【发布时间】:2018-05-02 18:41:31 【问题描述】:

我正在尝试以编程方式创建键盘附件视图。我已经设置了一个容器视图,并在其中尝试设置一个文本字段、发布按钮和一个表情符号。 这是我正在尝试制作的示例。

Click here to view the image.

这是我正在使用的代码。我认为问题出在我设置约束时。 我脑海中闪过的几个问题是:

    是否需要为容器视图设置约束?

    如何向文本字段添加适当的约束?

    override var inputAccessoryView: UIView? 
    get 
        //Set up the container
        let containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
        containerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
    
        let textField = UITextField()
        textField.placeholder = "Add a reframe..."
        textField.isSecureTextEntry = false
        textField.textAlignment = .left
        textField.borderStyle = .none
        textField.translatesAutoresizingMaskIntoConstraints = false
    
    
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
        textField.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
        textField.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
        textField.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
        textField.heightAnchor.constraint(equalToConstant: 50)
    
    
        containerView.addSubview(textField)
    
        return containerView
    
    
    

这是我不断遇到的错误。

*** 由于未捕获的异常“NSGenericException”而终止应用程序,原因:“无法使用锚激活约束,因为它们没有共同的祖先。约束或其锚点是否引用不同视图层次结构中的项目?这是非法的。'

编辑

查看帖子控制器

lazy var containerView: CommentInputAccessoryView = 
    let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
    let commentInputAccessoryView = CommentInputAccessoryView(frame: frame)
    commentInputAccessoryView.delegate = self
    return commentInputAccessoryView
()

//Setting up the keyboard accessory view for comments.
override var inputAccessoryView: UIView? 
    get 
        return containerView
    

override var canBecomeFirstResponder: Bool 
    return true

CommentInputAccessoryView

protocol CommentInputAccessoryViewDelegate 
func didSend(for comment: String)


class CommentInputAccessoryView: UIView 

var delegate: CommentInputAccessoryViewDelegate?

func clearCommentTextView() 
    commentTextView.text = nil
    showPlaceholderLabel()


let commentTextView: UITextView = 
    let text = UITextView()
    text.translatesAutoresizingMaskIntoConstraints = false
    //text.placeholder = "Add a reframe..."
    text.textAlignment = .left
    text.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
    text.layer.cornerRadius = 50/2
    text.layer.masksToBounds = true
    text.isScrollEnabled = false
    text.font = UIFont.systemFont(ofSize: 16)
    text.textContainerInset = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 64)
    //text.borderStyle = .none
    return text
()

let placeholderLabel: UILabel = 
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.text = "Add a response..."
    label.textColor = .black
    label.font = UIFont.systemFont(ofSize: 16)
    return label
()

func showPlaceholderLabel() 
    placeholderLabel.isHidden = false


let sendButton: UIButton = 
    let send = UIButton(type: .system)
    //let sendButton = UIImageView(image: #imageLiteral(resourceName: "arrowUp"))
    send.translatesAutoresizingMaskIntoConstraints = false
    send.setTitle("Send", for: .normal)
    send.setTitleColor(#colorLiteral(red: 0.2901960784, green: 0.3725490196, blue: 0.937254902, alpha: 1), for: .normal)
    send.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
    send.addTarget(self, action: #selector(handlePostComment), for: .touchUpInside)
    return send
()

let hugButton: UIButton = 
    let hug = UIButton()
    hug.translatesAutoresizingMaskIntoConstraints = false
    hug.setTitle("????", for: .normal)
    hug.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
    hug.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
    hug.layer.cornerRadius = 25
    hug.layer.masksToBounds = true
    return hug
()


override init(frame: CGRect) 
    super.init(frame: frame)
    autoresizingMask = .flexibleHeight

    addSubview(commentTextView)
    addSubview(sendButton)
    addSubview(hugButton)
    addSubview(placeholderLabel)


    if #available(ios 11.0, *) 
        commentTextView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
        hugButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
    
    else 
    

    commentTextView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
    commentTextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
    commentTextView.trailingAnchor.constraint(equalTo: hugButton.leadingAnchor, constant: 8)

    placeholderLabel.leadingAnchor.constraint(equalTo: commentTextView.leadingAnchor, constant: 18).isActive = true
    placeholderLabel.centerYAnchor.constraint(equalTo: self.commentTextView.centerYAnchor).isActive = true

    sendButton.trailingAnchor.constraint(equalTo: self.commentTextView.trailingAnchor, constant: -10).isActive = true
    sendButton.centerYAnchor.constraint(equalTo: self.commentTextView.bottomAnchor, constant: -22).isActive = true

    hugButton.leadingAnchor.constraint(equalTo: self.commentTextView.trailingAnchor, constant: 10).isActive = true
    hugButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -8).isActive = true
    hugButton.widthAnchor.constraint(equalToConstant: 40)
    //hugButton.centerYAnchor.constraint(equalTo: self.commentTextView.centerYAnchor).isActive = true

    NotificationCenter.default.addObserver(self, selector: #selector(handleTextChanged), name: .UITextViewTextDidChange, object: nil)


override var intrinsicContentSize: CGSize 
    return .zero


@objc func handleTextChanged() 
    placeholderLabel.isHidden = !self.commentTextView.text.isEmpty


@objc func handlePostComment() 
    guard let commentText = commentTextView.text else return
    delegate?.didSend(for: commentText)

required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")


这里有一些照片可能有助于了解正在发生的事情。

InputAccessoryView 工作: Click here

TextView 扩展: Click here.

【问题讨论】:

以下是面向未来观众的完整答案:这不是 pod。 github.com/29satnam/InputAccessoryView @Dipali,你是如何将最大高度设置为 UITextview 的? 我使用了您的逻辑,但无法显示分隔线。你遇到过这个问题吗? 【参考方案1】:

在应用约束之前,视图应该处于视图层次结构中,否则将引发错误。要消除错误,只需在 let textField = UITextField() 之后执行 containerView.addSubview(textField)

关于您发布的示例图片,最初的解决方案可能是这样的

override var inputAccessoryView: UIView? 
    get 
        //Set up the container
        let containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
        containerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)

        let textField = UITextField()
        containerView.addSubview(textField)
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.placeholder = "Add a reframe..."
        textField.textAlignment = .left
        textField.backgroundColor = .white
        textField.layer.cornerRadius = 50/2
        textField.layer.masksToBounds = true
        textField.borderStyle = .none
        textField.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8).isActive = true
        textField.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -5).isActive = true
        textField.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10).isActive = true
        textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: textField.frame.height)) // adding left padding so it's not sticked to border
        textField.leftViewMode = .always

        let arrow = UIImageView(image: #imageLiteral(resourceName: "arrowUp"))
        containerView.addSubview(arrow)
        arrow.translatesAutoresizingMaskIntoConstraints = false
        arrow.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: -10).isActive = true
        arrow.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true

        let button = UIButton()
        containerView.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("?", for: .normal)
        button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        button.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 10).isActive = true
        button.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8).isActive = true
        button.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true

        // Negative values for constraints can be avoided if we change order of views when applying constrains
        // f.e. instead of button.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8).isActive = true
        // write containerView.trailingAnchor.constraint(equalTo: button.trailingAnchor, constant: 8).isActive = true

        return containerView
    

【讨论】:

这太棒了!谢谢!我在 iphone x 上预览时遇到了一些问题,如何更改约束以对齐到安全位置而不是手机底部? @DipaliBajaj 很高兴它可以帮助您接受答案:)。完全忘记了 iPhone X。每个UIView 上都有safeAreaLayoutGuide 属性,可用于设置约束。例如,修复 emoji 按钮以使其在 iPhone X 的横向模式下可见是button.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true 感谢您的帮助。对此,我真的非常感激。我没有使用文本字段,而是包含了多行的文本视图。在模拟器中,当您在 CommentTextView 中键入多行时,它会展开并将按钮推出视图。你知道我该如何解决这个问题吗? @DipaliBajaj 不确定是什么导致了问题。您是否在控制台中有一些警告限制已被破坏? 没有警告!我不确定为什么它也在扩展。约束似乎是正确的。

以上是关于以编程方式创建输入附件视图的主要内容,如果未能解决你的问题,请参考以下文章

iOS:以编程方式在以编程方式创建的滚动视图中创建标签

以编程方式创建视图 VS。笔尖

以编程方式创建的视图不显示

如何以编程方式创建带有约束的视图

以编程方式创建视图后添加样式

如何以编程方式创建 UIImage 视图 - Swift