2016-05-14 15 views
11

Sfondo e descrizione del problema

Ho creato una vista di testo verticale da utilizzare con il mongolo. Si tratta di una visualizzazione testo personalizzata composta da tre livelli di viste: un bambino UITextView, una vista Contenitore (ruotata di 90 gradi e ruotata) per contenere lo UITextView e la vista padre. (Vedi here e here per ulteriori informazioni di sottofondo)Il contenuto di UITextView si perde con spazio extra dopo il ridimensionamento

La vista aumenta le dimensioni in base alle dimensioni del contenuto della vista testo sottostante purché sia ​​compresa tra una dimensione minima e una massima. Tuttavia, negli ultimi giorni ho faticato a correggere un bug in cui è stato aggiunto uno spazio extra e il contenuto è stato spostato a sinistra (ciò sarebbe dovuto alle coordinate della vista di testo sottostante). Questo può essere osservato nell'immagine seguente. La vista di colore giallo è la vista testo personalizzato (chiamato inputWindow nel codice vista del regolatore di seguito.)

enter image description here

Dopo si tocca entrare un paio di volte per aumentare la dimensione della visualizzazione dei contenuti, si aggiunge uno spazio aggiuntivo. Cercare di scorrere la vista non fa nulla. (Lo scorrimento funziona dopo che la larghezza raggiunge il suo valore massimo e la dimensione del contenuto è maggiore delle dimensioni del fotogramma.) È come se il contenuto fosse nel mezzo dello scorrimento quando è stato congelato sul posto prima di poter essere posizionato nella posizione corretta. Se inserisco un altro carattere (come uno spazio), la vista del contenuto si aggiorna nella posizione corretta.

Domanda

Cosa devo cambiare? O come posso forzare manualmente il sottostante UITextView per mostrare la sua visualizzazione del contenuto nella posizione corretta?

Codice

ho cercato di tagliare fuori tutto il codice estraneo e lasciare solo nelle parti rilevanti sia per il controller della vista e la TextView verticale personalizzato. Se c'è qualcos'altro che dovrei includere, fammi sapere.

View Controller

Il controller della vista aggiorna i vincoli di dimensione nella visualizzazione del testo personalizzato quando si tratta di contenuti cambiamenti Vedi a schermo.

import UIKit 
class TempViewController: UIViewController, KeyboardDelegate { 

    let minimumInputWindowSize = CGSize(width: 80, height: 150) 
    let inputWindowSizeIncrement: CGFloat = 50 

    // MARK:- Outlets 
    @IBOutlet weak var inputWindow: UIVerticalTextView! 
    @IBOutlet weak var topContainerView: UIView! 
    @IBOutlet weak var keyboardContainer: KeyboardController! 
    @IBOutlet weak var inputWindowHeightConstraint: NSLayoutConstraint! 
    @IBOutlet weak var inputWindowWidthConstraint: NSLayoutConstraint! 


    override func viewDidLoad() { 
     super.viewDidLoad() 

     // get rid of space at beginning of textview 
     self.automaticallyAdjustsScrollViewInsets = false 

     // setup keyboard 
     keyboardContainer.delegate = self 
     inputWindow.underlyingTextView.inputView = UIView() 
     inputWindow.underlyingTextView.becomeFirstResponder() 
    } 

    // KeyboardDelegate protocol 
    func keyWasTapped(character: String) { 
     inputWindow.insertMongolText(character) // code omitted for brevity 
     increaseInputWindowSizeIfNeeded() 
    } 
    func keyBackspace() { 
     inputWindow.deleteBackward() // code omitted for brevity 
     decreaseInputWindowSizeIfNeeded() 
    } 

    private func increaseInputWindowSizeIfNeeded() { 

     if inputWindow.frame.size == topContainerView.frame.size { 
      return 
     } 

     // width 
     if inputWindow.contentSize.width > inputWindow.frame.width && 
      inputWindow.frame.width < topContainerView.frame.size.width { 
      if inputWindow.contentSize.width > topContainerView.frame.size.width { 
       //inputWindow.scrollEnabled = true 
       inputWindowWidthConstraint.constant = topContainerView.frame.size.width 
      } else { 
       self.inputWindowWidthConstraint.constant = self.inputWindow.contentSize.width 
      } 
     } 

     // height 
     if inputWindow.contentSize.width > inputWindow.contentSize.height { 
      if inputWindow.frame.height < topContainerView.frame.height { 
       if inputWindow.frame.height + inputWindowSizeIncrement < topContainerView.frame.height { 
        // increase height by increment unit 
        inputWindowHeightConstraint.constant = inputWindow.frame.height + inputWindowSizeIncrement 
       } else { 
        inputWindowHeightConstraint.constant = topContainerView.frame.height 
       } 
      } 
     } 
    } 

    private func decreaseInputWindowSizeIfNeeded() { 

     if inputWindow.frame.size == minimumInputWindowSize { 
      return 
     } 

     // width 
     if inputWindow.contentSize.width < inputWindow.frame.width && 
      inputWindow.frame.width > minimumInputWindowSize.width { 

      if inputWindow.contentSize.width < minimumInputWindowSize.width { 
       inputWindowWidthConstraint.constant = minimumInputWindowSize.width 
      } else { 
       inputWindowWidthConstraint.constant = inputWindow.contentSize.width 
      } 
     } 

     // height 
     if (2 * inputWindow.contentSize.width) <= inputWindow.contentSize.height && inputWindow.contentSize.width < topContainerView.frame.width { 
      // got too high, make it shorter 
      if minimumInputWindowSize.height < inputWindow.contentSize.height - inputWindowSizeIncrement { 
       inputWindowHeightConstraint.constant = inputWindow.contentSize.height - inputWindowSizeIncrement 
      } else { 
       // Bump down to min height 
       inputWindowHeightConstraint.constant = minimumInputWindowSize.height 
      } 
     } 
    } 
} 

personalizzato Testo verticale Mostra

Questa visualizzazione personalizzata è fondamentalmente a shell around a UITextView per consentirgli di essere ruotato e girato per la corretta visualizzazione dei mongola tradizionale.

import UIKit 
@IBDesignable class UIVerticalTextView: UIView { 

    var textView = UITextView() 
    let rotationView = UIView() 

    var underlyingTextView: UITextView { 
     get { 
      return textView 
     } 
     set { 
      textView = newValue 
     } 
    } 


    var contentSize: CGSize { 
     get { 
      // height and width are swapped because underlying view is rotated 90 degrees 
      return CGSize(width: textView.contentSize.height, height: textView.contentSize.width) 
     } 
     set { 
      textView.contentSize = CGSize(width: newValue.height, height: newValue.width) 
     } 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

    override init(frame: CGRect){ 
     super.init(frame: frame) 
     self.setup() 
    } 

    override func awakeFromNib() { 
     super.awakeFromNib() 
     self.setup() 
    } 

    func setup() { 

     textView.backgroundColor = UIColor.yellowColor() 
     self.textView.translatesAutoresizingMaskIntoConstraints = false 
     self.addSubview(rotationView) 
     rotationView.addSubview(textView) 

     // add constraints to pin TextView to rotation view edges. 
     let leadingConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0) 
     let trailingConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0) 
     let topConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0) 
     let bottomConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0) 
     rotationView.addConstraints([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint]) 
    } 

    override func layoutSubviews() { 
     super.layoutSubviews() 

     rotationView.transform = CGAffineTransformIdentity 
     rotationView.frame = CGRect(origin: CGPointZero, size: CGSize(width: self.bounds.height, height: self.bounds.width)) 
     rotationView.userInteractionEnabled = true 
     rotationView.transform = translateRotateFlip() 
    } 

    func translateRotateFlip() -> CGAffineTransform { 

     var transform = CGAffineTransformIdentity 

     // translate to new center 
     transform = CGAffineTransformTranslate(transform, (self.bounds.width/2)-(self.bounds.height/2), (self.bounds.height/2)-(self.bounds.width/2)) 
     // rotate counterclockwise around center 
     transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2)) 
     // flip vertically 
     transform = CGAffineTransformScale(transform, -1, 1) 

     return transform 
    } 

} 

Quello che ho cercato

Molte delle idee per le cose che ho provato sono venuti da How do I size a UITextView to its content? In particolare, ho provato:

Impostazione del telaio, invece di layout automatico

Nella vista personalizzata metodo layoutSubviews() ho fatto

textView.frame = rotationView.bounds 

e non ho aggiunto i vincoli in setup(). Non c'era alcun effetto evidente.

allowsNonContiguousLayout

Questo ha avuto alcun effetto. (Suggerito here.)

textView.layoutManager.allowsNonContiguousLayout = false 

setNeedsLayout

Ho provato varie combinazioni di setNeedsLayout e setNeedsDisplay sul inputWindow e la vista testo sottostante.

inputWindow.setNeedsLayout() 
inputWindow.underlyingTextView.setNeedsLayout() 

anche all'interno di una dispatch_async in modo che venga eseguito sul prossimo ciclo di esecuzione.

dispatch_async(dispatch_get_main_queue()) { 
    self.inputWindow.setNeedsLayout() 
} 

sizeToFit

Facendo sizeToFit sul prossimo ciclo di fuga dopo l'aggiornamento del vincolo di larghezza sembrava promettente in un primo momento, ma ancora non ha risolto il problema. A volte il contenuto si congelava e in altri momenti sarebbe scorrevole. Non sempre si congela nello stesso punto ogni volta.

self.inputWindowWidthConstraint.constant = self.inputWindow.contentSize.width 
dispatch_async(dispatch_get_main_queue()) { 
    self.inputWindow.underlyingTextView.sizeToFit() 
} 

enter image description here

Ritardo

Ho cercato a scheduling a delayed event, ma questo si sente come un hack.

Un duplicato?

Una domanda analoga è UITextview gains an extra line when it should not. Tuttavia, è in Objective-C quindi non posso dire molto bene. Ha anche 6 anni senza risposta.

This answer menziona anche uno spazio aggiuntivo su iPhone 6+ (la mia immagine di prova sopra era iPhone 6, non 6+). Tuttavia, penso di aver provato i suggerimenti in quella risposta. Cioè, l'ho fatto

var _f = self.inputWindow.underlyingTextView.frame 
_f.size.height = self.inputWindow.underlyingTextView.contentSize.height 
self.inputWindow.underlyingTextView.frame = _f 

a nessun effetto notevole.

Aggiornamento: un progetto di base riproducibile

Al fine di rendere questo problema come riproducibili possibile, ho fatto un progetto autonomo. È disponibile su Github here. Il layout storyboard si presenta così:

enter image description here

Il giallo UIView classe è il inputWindow e deve essere impostato su UIVerticalTextView. La luce blu è la topContainerView. E i pulsanti sottostanti sostituiscono la tastiera.

Aggiungere i vincoli di autolayout visualizzati. Il vincolo di larghezza della finestra di input è 80 e il vincolo di altezza è 150.

Collegare le uscite e le azioni al codice del controller di visualizzazione riportato di seguito. Questo codice del controller della vista sostituisce completamente il codice del controller della vista che ho usato nel mio esempio originale sopra.

View Controller

import UIKit 
class ViewController: UIViewController { 

    let minimumInputWindowSize = CGSize(width: 80, height: 150) 
    let inputWindowSizeIncrement: CGFloat = 50 

    // MARK:- Outlets 
    @IBOutlet weak var inputWindow: UIVerticalTextView! 
    @IBOutlet weak var topContainerView: UIView! 
    //@IBOutlet weak var keyboardContainer: KeyboardController! 
    @IBOutlet weak var inputWindowHeightConstraint: NSLayoutConstraint! 
    @IBOutlet weak var inputWindowWidthConstraint: NSLayoutConstraint! 

    @IBAction func enterTextButtonTapped(sender: UIButton) { 
     inputWindow.insertMongolText("a") 
     increaseInputWindowSizeIfNeeded() 
    } 
    @IBAction func newLineButtonTapped(sender: UIButton) { 
     inputWindow.insertMongolText("\n") 
     increaseInputWindowSizeIfNeeded() 
    } 
    @IBAction func deleteBackwardsButtonTapped(sender: UIButton) { 
     inputWindow.deleteBackward() 
     decreaseInputWindowSizeIfNeeded() 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // get rid of space at beginning of textview 
     self.automaticallyAdjustsScrollViewInsets = false 

     // hide system keyboard but show cursor 
     inputWindow.underlyingTextView.inputView = UIView() 
     inputWindow.underlyingTextView.becomeFirstResponder() 
    } 

    private func increaseInputWindowSizeIfNeeded() { 

     if inputWindow.frame.size == topContainerView.frame.size { 
      return 
     } 

     // width 
     if inputWindow.contentSize.width > inputWindow.frame.width && 
      inputWindow.frame.width < topContainerView.frame.size.width { 
      if inputWindow.contentSize.width > topContainerView.frame.size.width { 
       //inputWindow.scrollEnabled = true 
       inputWindowWidthConstraint.constant = topContainerView.frame.size.width 
      } else { 
       self.inputWindowWidthConstraint.constant = self.inputWindow.contentSize.width 
      } 
     } 

     // height 
     if inputWindow.contentSize.width > inputWindow.contentSize.height { 
      if inputWindow.frame.height < topContainerView.frame.height { 
       if inputWindow.frame.height + inputWindowSizeIncrement < topContainerView.frame.height { 
        // increase height by increment unit 
        inputWindowHeightConstraint.constant = inputWindow.frame.height + inputWindowSizeIncrement 
       } else { 
        inputWindowHeightConstraint.constant = topContainerView.frame.height 
       } 
      } 
     } 
    } 

    private func decreaseInputWindowSizeIfNeeded() { 

     if inputWindow.frame.size == minimumInputWindowSize { 
      return 
     } 

     // width 
     if inputWindow.contentSize.width < inputWindow.frame.width && 
      inputWindow.frame.width > minimumInputWindowSize.width { 

      if inputWindow.contentSize.width < minimumInputWindowSize.width { 
       inputWindowWidthConstraint.constant = minimumInputWindowSize.width 
      } else { 
       inputWindowWidthConstraint.constant = inputWindow.contentSize.width 
      } 
     } 

     // height 
     if (2 * inputWindow.contentSize.width) <= inputWindow.contentSize.height && inputWindow.contentSize.width < topContainerView.frame.width { 
      // got too high, make it shorter 
      if minimumInputWindowSize.height < inputWindow.contentSize.height - inputWindowSizeIncrement { 
       inputWindowHeightConstraint.constant = inputWindow.contentSize.height - inputWindowSizeIncrement 
      } else { 
       // Bump down to min height 
       inputWindowHeightConstraint.constant = minimumInputWindowSize.height 
      } 
     } 
    } 
} 

UIVerticalTextView

Utilizzare lo stesso codice per la UIVerticalTextView nell'esempio originale, ma con l'aggiunta dei seguenti due metodi.

func insertMongolText(unicode: String) { 
    textView.insertText(unicode) 
} 

func deleteBackward() { 
    textView.deleteBackward() 
} 

prova

  1. Toccare "inserire testo" un paio di volte. (Si noti che il testo è retrocesso perché l'app effettiva utilizza un carattere speculare per compensare la visualizzazione del testo capovolto.)
  2. Toccare "nuova linea" cinque volte.
  3. Provare a scorrere la visualizzazione.

Osservare che il contenuto è fuori posto e che la vista non scorrerà.

Cosa devo fare per risolvere questo problema?

risposta

1

È possibile darci un progetto di esempio (su github)?

Potete provare con un cambiamento po 'al di sotto del codice del file UIVerticalTextView:

override func layoutSubviews() { 
    super.layoutSubviews() 

    rotationView.transform = CGAffineTransformIdentity 
    rotationView.frame = CGRect(origin: CGPointZero, size: CGSize(width: self.bounds.height, height: self.bounds.width)) 
    rotationView.userInteractionEnabled = true 
    rotationView.transform = translateRotateFlip() 

    if self.textView.text.isEmpty == false { 
     self.textView.scrollRangeToVisible(NSMakeRange(0, 1)) 
    } 
} 
+0

Il progetto di test è ora disponibile su Github: https://github.com/suragch/TestVerticalTextView – Suragch

+0

Non sono sicuro di cosa succede dopo le modifiche. La visualizzazione del testo non si blocca più. Diventa scorrevole senza ridimensionare. Potrebbe esserci qualcosa se esiste un altro modo per ridimensionare la vista testo. – Suragch

+0

@Suragch piccolo aggiornamento al layout Le visualizzazioni di UIVerticalTextView sembrano funzionare. – ZYiOS

0

Ho trovato una soluzione accettabile. Ha coinvolto

  1. cancellazione di Layout Automatico (all'interno del TextView personalizzato stessa) e
  2. l'aggiunta di un ritardo prima di aggiornare.

Nel progetto di esempio fornisce il seguente risultato.

enter image description here

Il contenuto della visualizzazione di testo aggiorna la sua posizione nella posizione corretta.

No layout automatico

Nella classe UIVerticalTextView ho commentato il layout automatico linee di vincolo:

let leadingConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0) 
let trailingConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0) 
let topConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0) 
let bottomConstraint = NSLayoutConstraint(item: self.textView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: rotationView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0) 
rotationView.addConstraints([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint]) 

Nota che questi sono i vincoli di layout automatico all'interno della visualizzazione personalizzata stesso (usato per marcare la textView frame ai limiti rotationView), non i vincoli di layout automatico dallo storyboard.

Così, invece di layout automatico, ho impostato textView.frame = rotationView.bounds in layoutSubviews:

override func layoutSubviews() { 
    super.layoutSubviews() 

    // ... 

    textView.frame = rotationView.bounds 
} 

Ritardo

Dopo gli aumenti di larghezza, ho aggiunto un ritardo di 100 millisecondi prima di chiamare setNeedsLayout.

private func increaseInputWindowSizeIfNeeded() { 

    // ... 

    // width 
    if inputWindow.contentSize.width > inputWindow.frame.width && 
     // ... 
     } else { 

      self.inputWindowWidthConstraint.constant = self.inputWindow.contentSize.width 

      // ********** Added delay here ********* 
      let delay = 0.1 
      let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))) 
      dispatch_after(time, dispatch_get_main_queue()) { 
       self.inputWindow.setNeedsLayout() 
      } 
      // ************************************* 

     } 
    } 

Stai ancora cercando una soluzione migliore

Impostazione delay = 0.1 opere nel simulatore, ma se ho impostato delay = 0.05 non funziona. Quindi, senza testarlo su tutti i dispositivi, non ho modo di sapere se il mio ritardo è abbastanza lungo. Per questo motivo, considero questa soluzione più un trucco piuttosto che una vera soluzione.

In ogni caso, non posso assegnare la taglia a me stesso, quindi se qualcuno può trovare una soluzione migliore, sarei lieto di sentirlo.

Problemi correlati