Ich habe eine UITextView
in meiner iOS-Anwendung, die eine große Menge Text anzeigt. Ich sage diesen Text dann mithilfe des Parameters für den Versatzrand der Variablen UITextView
. Mein Problem ist, dass die Auffüllung der UITextView
meine Berechnungen verwirrt, da sie sich abhängig von der Schriftgröße und der verwendeten Schriftart unterscheidet.
Daher stelle ich die Frage: Kann man die Auffüllung um den Inhalt der UITextView
entfernen?
Freuen Sie sich auf Ihre Antworten!
Es ist einer der dümmsten Fehler in iOS.
Die Klasse UITextViewFixed
ist normalerweise eine sinnvolle Lösung.
Hier ist die Klasse:
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}
}
Vergessen Sie nicht, scrollEnabled im Inspector auszuschalten!
Die Lösung funktioniert ordnungsgemäß im Storyboard
Die Lösung funktioniert zur Laufzeit ordnungsgemäß
In der Regel das sollte in den meisten Fällen alles sein.
Selbst wenn Sie die Höhe der Textansicht im laufenden Betrieb ändern, genügt dies normalerweise allen Anforderungen, die Sie benötigen.
(Ein allgemeines Beispiel für das Ändern der Höhe während des Vorgangs ist das Ändern der Höhe durch den Benutzer.)
Hier ist die kaputte UITextView von Apple ...
Hier ist UITextViewFixed
:
Beachten Sie, dass Sie natürlich müssen
Vergessen Sie nicht, scrollEnabled auszuschalten! :)
(1) In einigen ungewöhnlichen Fällen - zum Beispiel in einigen Fällen flexibler Zellenhöhen-Tabellensichten mit sich dynamisch ändernder Höhe - macht Apple das Ärgerlichste auf der Welt: sie fügen zusätzlichen Platz an der Unterseite hinzu .Nicht wirklich! Dies muss eines der aufregendsten Dinge in iOS sein.
Hier ist eine "schnelle Lösung" hinzuzufügen, die oft bei diesem Wahnsinn hilft.
...
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
// this is not ideal, but you can sometimes use this
// to fix the "space at bottom" insanity
var b = bounds
let h = sizeThatFits(CGSize(
width: bounds.size.width,
height: CGFloat.greatestFiniteMagnitude)
).height
b.size.height = h
bounds = b
...
(2) Um ein weiteres subtiles Apple-Durcheinander zu beheben, müssen Sie Folgendes hinzufügen:
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
super.setContentOffset(contentOffset, animated: false)
}
(3) Wir sollten hinzufügen:
contentInset = UIEdgeInsets.zero
gleich nach .lineFragmentPadding = 0
in UITextViewFixed
.
Aber ... glauben oder nicht ... es funktioniert einfach nicht im aktuellen iOS! (Im Jahr 2019 eingecheckt.) Diese Zeile muss möglicherweise in der Zukunft hinzugefügt werden.
Die Tatsache, dass UITextView
in iOS defekt ist, ist eines der seltsamsten Dinge in allen Bereichen des Mobile Computing. Zehn Jahre Jubiläum dieser Frage und noch immer nicht fixiert!
Zum Schluss noch ein ähnlicher Tipp für Text Field: https://stackoverflow.com/a/43099816/294884
Für iOS 7.0 habe ich festgestellt, dass der contentInset-Trick nicht mehr funktioniert. Dies ist der Code, den ich verwendet habe, um die Ränder/Polsterung in iOS 7 zu beseitigen.
Dadurch wird der linke Rand des Texts am linken Rand des Containers angezeigt:
textView.textContainer.lineFragmentPadding = 0
Dadurch wird der obere Rand des Texts am oberen Rand des Containers ausgerichtet
textView.textContainerInset = .zero
Beide Linien werden benötigt, um den Rand/die Auffüllung vollständig zu entfernen.
Diese Problemumgehung wurde 2009 geschrieben, als IOS 3.0 veröffentlicht wurde. Es gilt nicht mehr.
Ich stieß auf das gleiche Problem, am Ende musste ich es benutzen
nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);
dabei ist nameField eine UITextView
. Die Schriftart, die ich zufällig verwendete, war Helvetica 16 point. Es ist nur eine benutzerdefinierte Lösung für die jeweilige Feldgröße, die ich gezeichnet habe. Dadurch wird der linke Versatz bündig mit der linken Seite und der obere Versatz dort, wo ich ihn für die Box haben möchte, gezeichnet.
Außerdem scheint dies nur für UITextViews
zu gelten, wo Sie das Standard-Aligment verwenden, d.
nameField.textAlignment = NSTextAlignmentLeft;
Wenn Sie beispielsweise rechts ausrichten, scheint die Variable UIEdgeInsetsMake
keinerlei Auswirkungen auf den rechten Rand zu haben.
Zumindest können Sie mit der .contentInset -Eigenschaft Ihre Felder mit den "richtigen" Positionen platzieren und die Abweichungen berücksichtigen, ohne Ihre UITextViews
zu verschieben.
Ausgehend von den guten Antworten, die bereits gegeben wurden, handelt es sich um eine rein Interface Builder-basierte Lösung, die die benutzerdefinierten Laufzeitattribute verwendet und in iOS 7.0+ funktioniert:
Unter iOS 5 scheint UIEdgeInsetsMake(-8,-8,-8,-8);
großartig zu funktionieren.
Ich würde definitiv alle Antworten mit hartcodierten Werten vermeiden, da sich die tatsächlichen Ränder mit den Einstellungen der Schriftgröße des Benutzers usw. ändern können.
Hier ist die Antwort von @ user1687195, ohne den textContainer.lineFragmentPadding
zu ändern (da der docs -Zustand dies nicht der beabsichtigte Gebrauch ist).
Dies funktioniert hervorragend für iOS 7 und höher.
self.textView.textContainerInset = UIEdgeInsetsMake(
0,
-self.textView.textContainer.lineFragmentPadding,
0,
-self.textView.textContainer.lineFragmentPadding);
Dies ist praktisch das gleiche Ergebnis, nur ein wenig sauberer, da die lineFragmentPadding-Eigenschaft nicht missbraucht wird.
Storyboard- oder Interface Builder-Lösung mit benutzerdefinierten Laufzeitattributen . Update. Es wurden iOS7.1- und iOS6.1-Screenshots mit contentInset = {{-10, -5}, {0, 0}} .__ hinzugefügt.
Alle diese Antworten beziehen sich auf die Titelfrage, aber ich wollte einige Lösungen für die Probleme vorschlagen, die sich in der Frage der OP ergeben.
Um die Größe des Textes in UITextView
schnell zu berechnen, verwenden Sie NSLayoutManager
:
UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;
Dies ergibt den gesamten scrollbaren Inhalt, der größer sein kann als der Frame von UITextView
. Ich fand das viel genauer als textView.contentSize
, da es tatsächlich berechnet, wie viel Platz der Text benötigt. Geben Sie beispielsweise ein leeres UITextView
an:
textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)
UIFont
verfügt über eine Eigenschaft, mit der Sie schnell die Zeilenhöhe für die angegebene Schriftart ermitteln können. So können Sie die Zeilenhöhe des Texts in Ihrem UITextView
schnell finden mit:
UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;
Das Ermitteln der tatsächlich sichtbaren Textmenge ist für die Behandlung eines "Paging" -Effekts wichtig. UITextView
hat eine Eigenschaft mit dem Namen textContainerInset
, die eigentlich eine Grenze zwischen dem tatsächlichen UITextView.frame
und dem Text selbst ist. Um die tatsächliche Höhe des sichtbaren Rahmens zu berechnen, können Sie die folgenden Berechnungen durchführen:
UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;
Nachdem Sie nun über die sichtbare Textgröße und den Inhalt verfügen, können Sie schnell ermitteln, wie Ihre Offsets aussehen sollen, indem Sie textHeight
von textSize
abziehen:
// where n is the page number you want
CGFloat pageOffsetY = textSize - textHeight * (n - 1);
textView.contentOffset = CGPointMake(textView.contentOffset.x, pageOffsetY);
// examples
CGFloat page1Offset = 0;
CGFloat page2Offset = textSize - textHeight
CGFloat page3Offset = textSize - textHeight * 2
Mit all diesen Methoden habe ich meine Insets nicht angefasst und ich konnte an die Einfügemarke oder an einen beliebigen Ort im gewünschten Text gehen.
sie können die textContainerInset
-Eigenschaft von UITextView
verwenden:
textView.textContainerInset = UIEdgeInsetsMake (10, 10, 10, 10);
(oben links unten rechts)
Bei iOS 10 funktioniert die folgende Zeile für das Entfernen der oberen und unteren Auffüllung.
captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
Hier ist eine aktualisierte Version von Fatties sehr hilfreicher Antwort ... und fügt zwei sehr wichtige Zeilen hinzu, die mir dabei geholfen haben, perfektes Layout für iOS 10 und 11 (und wahrscheinlich auch für niedrigere) zu erstellen.
d
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
translatesAutoresizingMaskIntoConstraints = true
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
translatesAutoresizingMaskIntoConstraints = false
}
}
Die wichtigen Zeilen sind die beiden translatesAutoresizingMaskIntoConstraints = <true/false>
-Anweisungen!
Dies überraschend entfernt alle Ränder in allen meinen Umständen!
Während textView
kein First-Responder ist, kann es vorkommen, dass ein seltsamer unterer Rand vorhanden ist, der mit der in der akzeptierten Antwort erwähnten sizeThatFits
-Methode nicht gelöst werden konnte.
Beim Tippen auf das textView verschwand plötzlich der seltsame untere Rand und alles sah so aus, als sollte es aussehen, aber nur, wenn das textView firstResponder
hat.
Also lese ich hier auf SO , dass das Aktivieren und Deaktivieren von translatesAutoresizingMaskIntoConstraints
hilfreich ist, wenn der Frame/die Grenzen manuell zwischen den Aufrufen festgelegt werden.
Glücklicherweise funktioniert dies nicht nur mit der Frame-Einstellung, sondern auch mit den 2 Zeilen von setup()
, die zwischen den beiden translatesAutoresizingMaskIntoConstraints
-Aufrufen eingefügt sind!
Dies ist zum Beispiel sehr hilfreich bei der Berechnung des Frames einer Ansicht unter Verwendung von systemLayoutSizeFitting
für eine UIView
. Gibt die korrekte Größe zurück (was zuvor nicht der Fall war)!
Wie in der ursprünglichen Antwort erwähnt:
Vergiss nicht, scrollEnabled im Inspector auszuschalten!
Diese Lösung funktioniert sowohl im Storyboard als auch zur Laufzeit ordnungsgemäß.
Das ist es, jetzt sind Sie wirklich fertig!}
Für Swift 4, Xcode 9
Mit der folgenden Funktion können Sie den Rand/Abstand des Texts in UITextView ändern
public func UIEdgeInsetsMake (_ oben: CGFloat, _ links: CGFloat, _ unten: CGFloat, _ rechts: CGFloat) -> UIEdgeInsets
so in diesem Fall ist
self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
Neueste Swift:
self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
Hier ist eine einfache Erweiterung, die den Standardrand von Apple aus jeder Textansicht in Ihrer App entfernt.
Hinweis: Der Interface Builder zeigt immer noch den alten Rand an, Ihre App funktioniert jedoch wie erwartet.
extension UITextView {
open override func awakeFromNib() {
super.awakeFromNib();
removeMargins();
}
/** Removes the Apple textview margins. */
public func removeMargins() {
self.contentInset = UIEdgeInsetsMake(
0, -textContainer.lineFragmentPadding,
0, -textContainer.lineFragmentPadding);
}
}
Bei der Inset-Lösung hatte ich immer noch rechts und unten eine Polsterung. Auch die Textausrichtung verursachte Probleme. Der einzige sichere Weg, den ich gefunden habe, war, die Textansicht in eine andere, an Grenzen begrenzte Ansicht zu stellen.
Für mich (iOS 11 & Xcode 9.4.1) funktionierte das Zusammenstellen der Eigenschaft textView.font auf UIFont.preferred(forTextStyle:UIFontTextStyle)
style und die erste Antwort, wie von @Fattie erwähnt, magisch. Die @Fattie-Antwort hat jedoch nicht funktioniert, bis ich die Eigenschaft textView.font gesetzt habe. Andernfalls verhält sich UITextView unberechenbar.
Falls jemand nach der neuesten Swift-Version sucht, funktioniert der folgende Code einwandfrei mit Xcode 10.2 und Swift 4.2
yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)