2014-09-12 13 views
25

Lurker da molto tempo: poster per la prima volta!Modifica del frame di un inputAccessoryView in iOS 8

Ho un problema durante la ricreazione di una barra con un UITextView come WhatsApp lo fa.

Sto usando una consuetudine UIView sottoclasse, e pigramente istanziare su:

- (UIView *)inputAccessoryView

e il ritorno su SI:

- (BOOL)canBecomeFirstResponder

Ora, desidero modificare le dimensioni di inputAccessoryView quando la dimensione di UITextView aumenta. Su iOS 7, avrei semplicemente cambiato la dimensione della cornice di detta vista - e non la sua origine -, e quindi chiamata reloadInputViews e avrebbe funzionato: la vista sarebbe stata spostata verso l'alto in modo che fosse completamente visibile sopra la tastiera.

Su iOS 8, tuttavia, questo non funziona. L'unico modo per farlo funzionare è anche quello di cambiare l'origine del frame su un valore negativo. Questo andrebbe bene, tranne che crea alcuni bug strani: ad esempio, il UIView ritorna alla cornice 'originale' quando si immette qualsiasi testo.

C'è qualcosa che mi manca? Sono abbastanza sicuro che WhatsApp utilizzi inputAccessoryView per il modo in cui ignorano la tastiera durante il trascinamento, solo nell'ultima versione dell'app.

Per favore fatemi sapere se potete darmi una mano! O se c'è qualche prova che vorresti che io corra!

Grazie! :)

BTW, ecco il codice che sto usando per aggiornare l'altezza del costume UIView chiamato 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 

Edit: il codice sorgente completo è disponibile @https://github.com/manuelmenzella/SocketChat-iOS

risposta

30

Sono stato sbattere la testa contro il muro su questo per un bel po 'di tempo, come il comportamento è cambiato da iOS 7 a iOS 8. ho provato di tutto, fino a quando la soluzione più ovvia di tutto ha funzionato per me:

inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 

duh!

+0

stavo facendo esattamente la stessa cosa di te. Non penso che avrei pensato a questo. Stupefacente! – AdamPro13

+6

Ho usato il tuo suggerimento in combinazione con - (CGSize) intrinsicContentSize e ora funziona. Solo quella riga di codice non funzionerà. –

+3

E quindi chiamare -invalidateIntrinsicContentSize nella vista accessoria dopo aver apportato le modifiche. – kball

1

Sfortunatamente, iOS8 aggiunge un vincolo di altezza privato a inputAccessoryView e questo limite è non pubblico.

Si consiglia di ricreare la vista accessoria quando la sua cornice deve cambiare e chiamare reloadInputViews in modo che il nuovo sia installato.

Questo è quello che faccio e funziona come previsto.

+0

OK, è bello saperlo almeno! Grazie mille per il tuo aiuto! Ti dispiacerebbe condividere il codice che gestisce il ridimensionamento e lo spostamento della vista? –

+0

Come detto nel mio commento: quando la vista dell'accessorio di input dovrebbe cambiare il suo frame, sostituirlo. –

0

Per risolvere questo problema ho utilizzato inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

Ma ovviamente questo ha causato il crollo della mia vista testo. Aggiungendo quindi un vincolo alla barra degli strumenti e aggiornandolo quando è necessario, oppure aggiungendo il vincolo alla vista testo stessa e aggiornandolo ha funzionato per me.

1

Sì, iOS8 aggiunge un vincolo di altezza privato a inputAccessoryView.

Tenendo conto che ricreando tutta inputAccessoryView e sostituire vecchio è può essere un'operazione molto costoso, si può semplicemente rimuovere i vincoli prima vede ingresso ricarica

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

Solo un'altra soluzione

3

di funzionamento 100% e molto la soluzione semplice è enumerare tutti i vincoli e impostare un nuovo valore di altezza. Ecco del codice C# (Xamarin):

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

Il problema è che in iOS 8, un NSLayoutConstraint che imposta l'altezza del inputAccessoryView uguale alla sua altezza frame iniziale viene installato automaticamente. Per risolvere il problema di layout, è necessario aggiornare tale vincolo all'altezza desiderata e quindi indicare a inputAccessoryView l'orientamento.

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

hai duplicato la mia risposta –

+1

Non ho mai visto la tua risposta (ho 237 punti SO e sono stato su SO per ~ 4 anni, chiaramente non mi interessa abbastanza "duplicare" la tua risposta).Inoltre, la tua risposta è in C# (non comune per i client iOS) e non riesce a forzare l'inputAccessoryView per esporsi. –

+0

Hai lottato con questi problemi per giorni fino a quando non ho trovato la tua risposta. – JSNoob

0

Frist, ottenere inputAccessoryView e impostare nil

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

struttura e il layout quindi impostare di nuovo

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

ultimo set nuovo inputAccessoryView e ricaricare

yourTextView.inputAccessoryView = inputAccessoryView; 
[yourTextView reloadInputViews]; 
5

Ecco un completo , auto-cont soluzione ained (grazie @JohnnyC e @ JoãoNunes per avermi nella giusta direzione, @stigi per explaining come animare intrinsicContent modifiche):

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() 
     } 
    } 
} 
+0

grazie gentil signore! Funziona come un fascino! – Andres

+0

Penso che questa sia la soluzione migliore, ma sei stato in giro per il problema che la chiamata all'interno del blocco animato evita l'attivazione della notifica della tastiera? Pertanto non è possibile regolare le posizioni della vista di scorrimento ... – Omer

+0

@Omer no, non ricordo di aver riscontrato tale problema –

13

Per riassumere JohnnyC di answer: impostare l'inpitAccessoryView di autoresizingMask a .flexibleHeight, calcolare il suo intrinsicContentSize e lasciare che il quadro faccia il resto.

codice completo, aggiornato per 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() 
    } 

} 
+0

grazie, funziona per me! stato un piccolo cambiamento, in Swift 3 intristicContentSize non è più una funzione ora è una variabile. –

+0

@ChristianSchober grazie per averlo notato, ho aggiornato il codice per Swift 3. – maxkonovalov