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.)
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()
}
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ì:
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
- 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.)
- Toccare "nuova linea" cinque volte.
- Provare a scorrere la visualizzazione.
Osservare che il contenuto è fuori posto e che la vista non scorrerà.
Cosa devo fare per risolvere questo problema?
Il progetto di test è ora disponibile su Github: https://github.com/suragch/TestVerticalTextView – Suragch
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
@Suragch piccolo aggiornamento al layout Le visualizzazioni di UIVerticalTextView sembrano funzionare. – ZYiOS