Подтвердить что ты не робот

Изменение фрейма 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

4b9b3361

Ответ 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];