Изменение фрейма inputAccessoryView в iOS 8
Долгое время lurker - первый раз постер!
У меня возникла проблема при воссоздании панели с помощью UITextView
, например WhatsApp.
Я использую собственный подкласс UIView
и лениво создаю его:
- (UIView *)inputAccessoryView
и возвращает YES:
- (BOOL)canBecomeFirstResponder
Теперь я хочу изменить размер inputAccessoryView
, когда размер UITextView
растет. На iOS 7 я просто изменил бы размер фрейма указанного представления, а не его начало, а затем позвонил reloadInputViews
, и он будет работать: вид будет перемещен вверх, чтобы он был полностью виден над клавиатурой.
Однако на iOS 8 это не работает. Единственный способ заставить его работать - это также изменить начало кадра на отрицательное значение. Это было бы хорошо, за исключением того, что он создавал некоторые странные ошибки: например, UIView
возвращается к исходному кадру при вводе любого текста.
Есть ли что-то, что мне не хватает? Я довольно уверен, что WhatsApp использует inputAccessoryView
из-за того, как они отклоняют клавиатуру при перетаскивании - только в последней версии приложения.
Пожалуйста, дайте мне знать, если вы можете мне помочь! Или если есть какой-либо тест, который вы хотели бы мне запустить!
Спасибо!:)
Кстати, вот код, который я использую, чтобы обновить высоту пользовательского UIView, называемого composeBar
:
// 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
Ответы
Ответ 1
Я довольно долго стучал головой о стену на этом, так как поведение менялось с iOS 7 на iOS 8. Я пробовал все, пока не получилось наиболее очевидное решение для меня:
inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
Дух!
Ответ 2
Подводя итог 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 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()
}
}
Ответ 3
Проблема в том, что в iOS 8 NSLayoutConstraint, который устанавливает высоту входного окна, равную начальной высоте кадра, устанавливается автоматически. Чтобы исправить проблему с макетом, вам необходимо обновить это ограничение до нужной высоты, а затем проинструктировать ваш входной интерфейс AccessAccessoryView.
- (void)changeInputAccessoryView:(UIView *)inputAccessoryView toHeight:(CGFloat)height {
for (NSLayoutConstraint *constraint in [inputAccessoryView constraints]) {
if (constraint.firstAttribute == NSLayoutAttributeHeight) {
constraint.constant = height;
[inputAccessoryView layoutIfNeeded];
break;
}
}
}
Ответ 4
Здесь полное, автономное решение (спасибо @JohnnyC и @JoãoNunes, указав меня в нужном направлении, @stigi для объяснение, как оживить изменения intrinsicContent
):
class InputAccessoryView: UIView {
// InputAccessoryView is instantiated from nib, but it 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()
}
}
}
Ответ 5
100% работая и очень простое решение - перечислить все ограничения и установить новое значение высоты. Вот код С# (xamarin):
foreach (var constraint in inputAccessoryView.Constraints)
{
if (constraint.FirstAttribute == NSLayoutAttribute.Height)
{
constraint.Constant = newHeight;
}
}
Ответ 6
К сожалению, iOS8 добавляет личное ограничение высоты для inputAccessoryView, и это ограничение не является общедоступным.
Я рекомендую воссоздать аксессуар, когда его кадр должен измениться, и вызвать reloadInputViews, чтобы новый был установлен.
Это то, что я делаю, и работает как ожидалось.
Ответ 7
Yep, iOS8 добавляет личное ограничение высоты в inputAccessoryView.
Принимая во внимание, что воссоздание целого inputAccessoryView и замена старого может быть действительно дорогостоящей операцией, вы можете просто удалить ограничения перед просмотром входных просмотров
[inputAccessoryView removeConstraints:[inputAccessoryView constraints]];
[textView reloadInputViews];
Еще одно обходное решение
Ответ 8
Чтобы исправить это, я использовал inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
Но, конечно, это привело к краху моего текста.
Поэтому добавление ограничения на панель инструментов и ее обновление, когда мне нужно, или добавление ограничения к самому текстуру и его обновление, сработало для меня.
Ответ 9
frist, получите inputAccessoryView и установите nil
UIView *inputAccessoryView = yourTextView.inputAccessoryView;
yourTextView.inputAccessoryView = nil;
[yourTextView reloadInputViews];
затем установите рамку и макет
inputAccessoryView.frame = XXX
[inputAccessoryView setNeedsLayout];
[inputAccessoryView layoutIfNeeded];
последний новый набор newAccessoryView снова и перезагрузите
yourTextView.inputAccessoryView = inputAccessoryView;
[yourTextView reloadInputViews];