【问题标题】:Changing the frame of an inputAccessoryView in iOS 8在 iOS 8 中更改 inputAccessoryView 的框架
【发布时间】:2014-11-07 03:31:17
【问题描述】:

长期潜伏者 - 第一次海报!

我在使用UITextView 重新创建一个栏时遇到问题,就像 WhatsApp 那样。

我正在使用自定义的UIView 子类,并懒惰地实例化它:

- (UIView *)inputAccessoryView

并返回 YES:

- (BOOL)canBecomeFirstResponder

现在,当UITextView 的大小增加时,我想更改inputAccessoryView 的大小。在 iOS 7 上,我只需更改所述视图框架的大小 - 而不是它的原点 - 然后调用 reloadInputViews 它将起作用:视图将向上移动,使其在键盘上方完全可见。

但是,在 iOS 8 上,这不起作用。使其工作的唯一方法是将框架的原点也更改为负值。这很好,除了它会产生一些奇怪的错误:例如,UIView 在输入任何文本时返回到“原始”框架。

我有什么遗漏吗?我很确定 WhatsApp 使用 inputAccessoryView,因为它们在拖动时会关闭键盘 - 仅在最新版本的应用程序中。

如果你能帮助我,请告诉我!或者,如果您希望我进行任何测试!

谢谢! :)

顺便说一句,这是我用来更新名为 composeBar 的自定义 UIView 的高度的代码:

// ComposeBar frame size
CGRect frame = self.composeBar.frame;
frame.size.height += heightDifference;
frame.origin.y -= heightDifference;
self.composeBar.frame = frame;
[self.composeBar.textView reloadInputViews]; // Tried with this
[self reloadInputViews];                     // and this

编辑:完整源代码可用@https://github.com/manuelmenzella/SocketChat-iOS

【问题讨论】:

    标签: ios objective-c ios8 inputaccessoryview


    【解决方案1】:

    总结一下 JohnnyC 的 answer:将你的 inpitAccessoryView 的 autoresizingMask 设置为 .flexibleHeight,计算它的 intrinsicContentSize,然后让框架完成剩下的工作。

    完整代码,针对 Swift 3 更新:

    class InputAccessoryView: UIView, UITextViewDelegate {
    
        let textView = UITextView()
    
        override var intrinsicContentSize: CGSize {
            // Calculate intrinsicContentSize that will fit all the text
            let textSize = textView.sizeThatFits(CGSize(width: textView.bounds.width, height: CGFloat.greatestFiniteMagnitude))
            return CGSize(width: bounds.width, height: textSize.height)
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            // This is required to make the view grow vertically
            autoresizingMask = .flexibleHeight
    
            // Setup textView as needed
            addSubview(textView)
            textView.translatesAutoresizingMaskIntoConstraints = false
            addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[textView]|", options: [], metrics: nil, views: ["textView": textView]))
            addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[textView]|", options: [], metrics: nil, views: ["textView": textView]))
    
            textView.delegate = self
    
            // Disabling textView scrolling prevents some undesired effects,
            // like incorrect contentOffset when adding new line,
            // and makes the textView behave similar to Apple's Messages app
            textView.isScrollEnabled = false
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        // MARK: UITextViewDelegate
    
        func textViewDidChange(_ textView: UITextView) {
            // Re-calculate intrinsicContentSize when text changes
            invalidateIntrinsicContentSize()
        }
    
    }
    

    【讨论】:

    • 谢谢,为我工作!有一点点变化,在 Swift 3 中 intristicContentSize 不再是一个函数,它现在是一个变量。
    • @ChristianSchober 感谢您注意到这一点,我更新了 Swift 3 的代码。
    【解决方案2】:

    这是一个完整的、独立的解决方案(感谢 @JohnnyC@JoãoNunes 为我指明了正确的方向,@stigi explaining如何动画intrinsicContent变化):

    class InputAccessoryView: UIView {
    
        // InputAccessoryView is instantiated from nib, but it's not a requirement
        override func awakeFromNib() {
            super.awakeFromNib()        
            autoresizingMask = .FlexibleHeight
        }
    
        override func intrinsicContentSize() -> CGSize {
            let exactHeight = // calculate exact height of your view here
            return CGSize(width: UIViewNoIntrinsicMetric, height: exactHeight)
        }
    
        func somethingDidHappen() {
            // invalidate intrinsic content size, animate superview layout        
            UIView.animateWithDuration(0.2) {
                self.invalidateIntrinsicContentSize()
                self.superview?.setNeedsLayout()
                self.superview?.layoutIfNeeded()
            }
        }
    }
    

    【讨论】:

    • 我认为这也是更好的解决方案,但是您是否遇到过在动画块内调用避免触发键盘通知的问题?因此无法调整滚动视图位置...
    • @Omer 不,我不记得有这样的问题
    • :( ...也许它与iOS 9有关,很奇怪。
    【解决方案3】:

    首先,获取 inputAccessoryView 并设置 nil

    UIView *inputAccessoryView = yourTextView.inputAccessoryView;
    yourTextView.inputAccessoryView = nil;
    [yourTextView reloadInputViews];
    

    然后设置框架和布局

    inputAccessoryView.frame = XXX
    [inputAccessoryView setNeedsLayout];
    [inputAccessoryView layoutIfNeeded];
    

    最后再次设置新的 inputAccessoryView 并重新加载

    yourTextView.inputAccessoryView = inputAccessoryView;
    [yourTextView reloadInputViews];
    

    【讨论】:

      【解决方案4】:

      问题在于,在 iOS 8 中,会自动安装一个 NSLayoutConstraint,它将 inputAccessoryView 的高度设置为其初始帧高度。为了解决布局问题,您需要将该约束更新为所需的高度,然后指示您的 inputAccessoryView 自行布局。

      - (void)changeInputAccessoryView:(UIView *)inputAccessoryView toHeight:(CGFloat)height {
          for (NSLayoutConstraint *constraint in [inputAccessoryView constraints]) {
              if (constraint.firstAttribute == NSLayoutAttributeHeight) {
                  constraint.constant = height;
                  [inputAccessoryView layoutIfNeeded];
                  break;
              }
          }
      }
      

      【讨论】:

      • 我从未见过你的答案(我有 237 分,并且已经在 SO 上工作了大约 4 年;显然我不太在意“复制”你的答案)。此外,您的答案在 C# 中(对于 iOS 客户端来说并不常见),并且无法强制 inputAccessoryView 自行布局。
      • 几天来一直在为这个问题苦苦挣扎,直到我找到你的答案。
      【解决方案5】:

      随着行为从 iOS 7 到 iOS 8 的变化,我已经在这个问题上碰了很长时间。我尝试了所有方法,直到最明显的解决方案对我有用:

      inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
      

      呃!

      【讨论】:

      • 我和你做的一模一样。我不认为我会想到这一点。太棒了!
      • 我将您的建议与 - (CGSize)intrinsicContentSize 结合使用,现在它可以工作了。只有那行代码不起作用。
      • 然后在您进行更改后在附件视图上调用 -invalidateIntrinsicContentSize。
      • 不错的解决方案!这是我的回答中一个完整的代码示例:stackoverflow.com/a/32647908/3160561
      • '感谢!这是最好的解决方案。
      【解决方案6】:

      100% 有效且非常简单的解决方案是枚举所有约束并设置新的高度值。这是一些 C# 代码(xamarin):

      foreach (var constraint in inputAccessoryView.Constraints)
      {
          if (constraint.FirstAttribute == NSLayoutAttribute.Height)
          {
              constraint.Constant = newHeight;
          }
      }
      

      【讨论】:

        【解决方案7】:

        是的,iOS8 为 inputAccessoryView 添加了一个私有高度约束。

        考虑到重新创建整个 inputAccessoryView 并替换旧的可能是非常昂贵的操作,您可以在重新加载输入视图之前删除约束

        [inputAccessoryView removeConstraints:[inputAccessoryView constraints]];
        [textView reloadInputViews];
        

        只是另一种解决方法

        【讨论】:

          【解决方案8】:

          为了解决这个问题,我使用了 inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

          但这当然导致我的文本视图崩溃。 因此,向工具栏添加约束并在需要时对其进行更新,或者将约束添加到文本视图本身并对其进行更新对我有用。

          【讨论】:

            【解决方案9】:

            不幸的是,iOS8 给 inputAccessoryView 添加了一个私有高度约束,而这个约束不是公共的。

            我建议在框架应该改变时重新创建附件视图,并调用 reloadInputViews 以便安装新的。

            这就是我所做的,它按预期工作。

            【讨论】:

            • 好的,至少知道这一点很棒!非常感谢您的帮助!您介意分享处理视图大小调整和移动的代码吗?
            • 正如我在评论中所说:当输入附件视图应该改变它的框架时,替换它。
            猜你喜欢
            • 2015-03-08
            • 1970-01-01
            • 2015-08-04
            • 1970-01-01
            • 2014-11-23
            • 2014-11-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多