更改以编程方式创建的自定义 inputAccessoryView 的高度?迅速 4

Posted

技术标签:

【中文标题】更改以编程方式创建的自定义 inputAccessoryView 的高度?迅速 4【英文标题】:change height of programmatically created custom inputAccessoryView? swift 4 【发布时间】:2018-04-25 13:57:20 【问题描述】:

所以我正在制作一个消息应用程序,我有 4 个类属性:

    typingView: UIView! messageTextView: UITextView! sendButton: UIButton! typingViewHeight: NSLayoutConstraint?

typingView 是主视图的inputAccessoryView,它有效!它包含messageTextViewsendButton。但我的问题是......当messageTextView 中的文本行数增加时,我希望高度随着动画的增加而增加(我暂时忽略减少)。通过这样做,我决定更改typingView 的高度。

检测 contentSize 的变化非常有效,我添加了一个观察者 添加这一行

messageTextView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

并用这个覆盖来收听它

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)

在这个函数内部,我有(这是一个过度简化和伪代码,假设它有效)

UIView.animate(withDuration: keyboardAnimationDuration) 
    self.typingViewHeight?.constaint += (messageTextView.font?.lineHeight)!

这是我自定义inputAccessoryView的代码:

lazy var typingViewContainer: UIView = 
    // Set up typing view
    typingView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 55))
    typingView.backgroundColor = UIColor.darkGray
    typingView.translatesAutoresizingMaskIntoConstraints = false

    // Set up animated constraints
    typingViewHeight = typingView.heightAnchor.constraint(equalToConstant: typingView.frame.height)
    typingViewHeight?.isActive = true

    // Set up text view
    messageTextView = UITextView()
    messageTextView.translatesAutoresizingMaskIntoConstraints = false
    messageTextView.font = fakeMessageTextView.font
    messageTextView.keyboardType = fakeMessageTextView.keyboardType
    messageTextView.isScrollEnabled = fakeMessageTextView.isScrollEnabled
    messageTextView.alwaysBounceVertical = fakeMessageTextView.alwaysBounceVertical
    messageTextView.text = fakeMessageTextView.text
    messageTextView.textColor = fakeMessageTextView.textColor
    messageTextView.delegate = self
    typingView.addSubview(messageTextView)

    // Constraints
    messageTextView.bottomAnchor.constraint(equalTo: typingView.bottomAnchor, constant: -fakeMessageTextViewBottom.constant).isActive = true
    messageTextView.topAnchor.constraint(equalTo: typingView.topAnchor, constant: fakeMessageTextViewTop.constant).isActive = true
    messageTextView.leftAnchor.constraint(equalTo: typingView.leftAnchor, constant: fakeMessageTextViewBottom.constant).isActive = true
    messageTextView.widthAnchor.constraint(equalToConstant: fakeMessageTextViewWidth.constant).isActive = true

    // Observers
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)  
    messageTextView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

    // Set up send button
    sendButton = UIButton(type: fakeSendButton.buttonType)
    sendButton.translatesAutoresizingMaskIntoConstraints = false
    sendButton.setTitle(fakeSendButton.titleLabel?.text!, for: .normal)
    sendButton.titleLabel?.font = fakeSendButton.titleLabel?.font
    sendButton.titleLabel?.shadowColor = fakeSendButton.titleLabel?.shadowColor
    sendButton.addTarget(self, action: #selector(sendPressed(_:)), for: .touchUpInside)
    typingView.addSubview(sendButton)

    // Constraints
    sendButton.heightAnchor.constraint(equalToConstant: fakeSendButtonHeight.constant).isActive = true
    sendButton.widthAnchor.constraint(equalToConstant: fakeSendButtonWidth.constant).isActive = true
    sendButton.bottomAnchor.constraint(equalTo: typingView.bottomAnchor, constant: -fakeSendButtonBottom.constant).isActive = true
    sendButton.rightAnchor.constraint(equalTo: typingView.rightAnchor, constant: -fakeSendButtonRight.constant).isActive = true

    return typingView
()

override var inputAccessoryView: UIView? 
    get 
        return typingViewContainer
    


override var canBecomeFirstResponder: Bool 
    get 
        return true
    

但是当我测试这个并输入多行时,它会在控制台中打印出来:

2018-04-25 08:52:57.614383-0500 ProxiChat[3351:168967] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
    (1) look at each constraint and try to figure out which you don't expect; 
    (2) find the code that added the unwanted constraint or constraints and fix it. 
(
"<NSLayoutConstraint:0x608000280a50 UIView:0x7f8528a2cda0.height == 73   (active)>",
"<NSLayoutConstraint:0x6040000993c0 UIView:0x7f8528a2cda0.height == 55   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x608000280a50 UIView:0x7f8528a2cda0.height == 73   
(active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

为什么有两个约束?我只添加了一个。请帮忙,谢谢!

编辑

假设我更改typingView 高度的代码有效,因为它在我将视图更改为自定义inputAccessoryView 之前有效。我只想知道为什么它会打印出那个错误,以及如何通过设置两个约束来修复它,即使我只添加了一个?

【问题讨论】:

你找到解决这个问题的方法了吗?我处于确切的情况,找不到答案。 不幸的是,这似乎是不可能的。抱歉,这是 ios 限制! 【参考方案1】:

当您创建视图时,您将高度设置为 55:

typingView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 55))

然后你正在设置一个高度锚:

typingViewHeight = typingView.heightAnchor.constraint(equalToConstant: typingView.frame.height)
typingViewHeight?.isActive = true

这就是您看到冲突的原因。另一个观察结果是,在设置高度锚时,您可以在创建它的同一行中激活它:

typingViewHeight = typingView.heightAnchor.constraint(equalToConstant: typingView.frame.height).isActive = true

这样可以避免您每次都编写额外的代码行

【讨论】:

你不能这样做来获得约束typingViewHeight = typingView.heightAnchor.constraint(equalToConstant: typingView.frame.height).isActive = true 这对我不起作用。另外,如果我尝试typingView = UIView(),而不是"&lt;NSLayoutConstraint:0x6040000993c0 UIView:0x7f8528a2cda0.height == 55 (active)&gt;",它会显示"&lt;NSLayoutConstraint:0x6040000993c0 UIView:0x7f8528a2cda0.height == 0 (active)&gt;"。即使我创建了一个没有初始化的 UIView(),它也会将默认高度设置为零?【参考方案2】:

首要任务

我建议您使用UITextView 的委托方法而不是观察来调整容器和文本视图的高度。该代码的作用相同,但我认为它更简洁明了。

func textViewDidChange(_ textView: UITextView) 
        let sizeToFitIn = CGSize(width: textView.bounds.size.width, height: .greatestFiniteMagnitude)
        let newSize = textView.sizeThatFits(sizeToFitIn)
        var newHeight = newSize.height

        ....

        // That part depends on your approach to placing constraints, it's just my example
        textInputHeightConstraint?.constant = newHeight
        inputContainerHeightConstraint?.constant = newHeight + (interfaceFactory.inputContainerHeight - interfaceFactory.textInputMinimumHeight)
    

尝试像这样更新您的代码:

typingViewHeight = typingView.heightAnchor.constraint(equalToConstant: typingView.frame.height)
typingViewHeight?.priority = UILayoutPriority(rawValue: 999)
typingViewHeight?.isActive = true

如果没有帮助,请查看该答案: https://***.com/a/27522511/5147552

【讨论】:

处理 UITextView 的 contentSize 更改的代码基于我编写的许多其他代码,因此在此处发布会有些困难。不过,我知道它可以工作,因为它与 UITextView 一起工作之前我添加了一个自定义inputAccessoryView。我的问题是,假设我正在正确调整 typingViewnot messageTextView)的高度,为什么它会给我这个错误而不让我改变高度约束?跨度>

以上是关于更改以编程方式创建的自定义 inputAccessoryView 的高度?迅速 4的主要内容,如果未能解决你的问题,请参考以下文章

在 xib 中以编程方式自定义 UIView

删除以编程方式创建的自定义按钮的边框

以编程方式约束 Dynamic ViewController 中的自定义 UIView

创建具有多个标签的自定义按钮

设置 UIBarButtonItem 的背景图像以编程方式更改其大小

如何在 SWIFT 中以编程方式将我的自定义控制器变成根控制器? (无法加载 NIB)