J'ai une UITextView
dans mon application iOS, qui affiche une grande quantité de texte. Je recherche ensuite ce texte en utilisant le paramètre de marge de décalage de la variable UITextView
. Mon problème est que le remplissage de la UITextView
confond mes calculs car il semble être différent selon la taille de la police et la police de caractères que j'utilise.
Par conséquent, je pose la question: Est-il possible de supprimer le remplissage entourant le contenu de la variable UITextView
?
Au plaisir d'entendre vos réponses!
C'est l'un des bugs les plus stupides d'iOS.
La classe UITextViewFixed
est généralement une solution raisonnable.
Voici la classe:
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}
}
N'oubliez pas de désactiver scrollEnabled dans l'inspecteur!
La solution fonctionne correctement dans le storyboard
La solution fonctionne correctement à l'exécution
En général, cela devrait suffire dans la plupart des cas.
Même si vous modifiez la hauteur de la vue texte à la volée, tout ce dont vous avez besoin est généralement nécessaire.
(Un exemple courant de changement de hauteur à la volée est de le modifier à mesure que l'utilisateur le tape.)
Voici le UITextView cassé d'Apple ...
Voici UITextViewFixed
:
Notez que vous devez bien sûr
N'oubliez pas de désactiver scrollEnabled! :)
(1) Dans certains cas inhabituels - par exemple, certains cas de vues de table de hauteur de cellule flexibles avec une hauteur qui change de façon dynamique - Apple effectue la chose la plus exaspérante au monde: ils ajoutent de l'espace supplémentaire au bas .Pas vraiment! Cela devrait être l’une des choses les plus exaspérantes d’iOS.
Voici une "solution rapide" à ajouter qui aide souvent avec cette folie.
...
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) Parfois, pour réparer encore un subtil désordre Apple, vous devez ajouter ceci:
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
super.setContentOffset(contentOffset, animated: false)
}
(3) On peut dire que nous devrions ajoutons:
contentInset = UIEdgeInsets.zero
juste après .lineFragmentPadding = 0
dans UITextViewFixed
.
Cependant ... crois ou pas ... ça ça ne marche pas sous iOS! (Vérifié en 2019.) Il sera peut-être nécessaire d'ajouter cette ligne à l'avenir.
Le fait que UITextView
soit cassé dans iOS est l’une des choses les plus étranges de l’informatique mobile. Le dixième anniversaire de cette question et ce n'est toujours pas réglé!
Enfin, voici un conseil quelque peu similaire pour TextField: https://stackoverflow.com/a/43099816/294884
Pour iOS 7.0, j'ai constaté que l'astuce contentInset ne fonctionnait plus. C'est le code que j'ai utilisé pour supprimer la marge/remplissage dans iOS 7.
Cela amène le bord gauche du texte au bord gauche du conteneur:
textView.textContainer.lineFragmentPadding = 0
Cela aligne le haut du texte sur le haut du conteneur.
textView.textContainerInset = .zero
Les deux lignes sont nécessaires pour supprimer complètement la marge/remplissage.
Cette solution de contournement a été écrite en 2009 lorsque IOS 3.0 a été publié. Cela ne s'applique plus.
Je suis tombé sur le même problème, à la fin j'ai dû utiliser
nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);
où nameField est une UITextView
. La police que j'ai utilisée était Helvetica 16 Point. Ce n'est qu'une solution personnalisée pour la taille de champ particulière que je dessinais. Cela rend le décalage gauche aligné avec le côté gauche, et le décalage supérieur où je le veux pour la boîte son tirage po
En outre, cela ne semble s'appliquer qu'à UITextViews
où vous utilisez l'alignement par défaut, c'est-à-dire.
nameField.textAlignment = NSTextAlignmentLeft;
Alignez à droite par exemple et la UIEdgeInsetsMake
semble n'avoir aucun impact sur le bord droit.
À tout le moins, l’utilisation de la propriété .contentInset vous permet de placer vos champs avec les positions "correctes" et de gérer les écarts sans décaler votre UITextViews
.
En se basant sur certaines des bonnes réponses déjà données, voici une solution purement Interface Builder qui utilise les attributs d’exécution définis par l’utilisateur et fonctionne sous iOS 7.0+:
Sur iOS 5, UIEdgeInsetsMake(-8,-8,-8,-8);
semble bien fonctionner.
Je voudrais absolument éviter toute réponse impliquant des valeurs codées en dur, car les marges réelles peuvent changer avec les paramètres de taille de police de l'utilisateur, etc.
Voici la réponse de @ user1687195, écrite sans modifier le textContainer.lineFragmentPadding
(car le docs state n’est pas l’utilisation prévue).
Cela fonctionne très bien pour iOS 7 et les versions ultérieures.
self.textView.textContainerInset = UIEdgeInsetsMake(
0,
-self.textView.textContainer.lineFragmentPadding,
0,
-self.textView.textContainer.lineFragmentPadding);
C'est effectivement le même résultat, juste un peu plus propre en ce sens qu'il n'abuse pas de la propriété lineFragmentPadding.
Storyboard ou Interface Builder, utilisant les attributs d'exécution définis par l'utilisateur . Mettre à jour. Ajout de captures d'écran iOS7.1 et iOS6.1 avec contentInset = {{-10, -5}, {0, 0}}
Toutes ces réponses répondent à la question du titre, mais je voulais proposer des solutions aux problèmes présentés dans le corps de la question du PO.
Un moyen rapide de calculer la taille du texte à l'intérieur de la UITextView
consiste à utiliser la NSLayoutManager
:
UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;
Cela donne le contenu total pouvant défiler, qui peut être plus grand que le cadre de la UITextView
. J'ai trouvé que c'était beaucoup plus précis que textView.contentSize
puisqu'il calcule réellement l'espace occupé par le texte. Par exemple, si la variable UITextView
est vide:
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
a une propriété qui vous permet d'obtenir rapidement la hauteur de ligne pour la police donnée. Vous pouvez donc trouver rapidement la hauteur de ligne du texte dans votre UITextView
avec:
UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;
Déterminer la quantité de texte réellement visible est important pour gérer un effet de "pagination". UITextView
a une propriété appelée textContainerInset
qui est en fait une marge entre le UITextView.frame
réel et le texte lui-même. Pour calculer la hauteur réelle du cadre visible, vous pouvez effectuer les calculs suivants:
UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;
Enfin, maintenant que vous avez la taille et le contenu du texte visibles, vous pouvez déterminer rapidement ce que vos décalages devraient être en soustrayant la textHeight
de la textSize
:
// 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
En utilisant toutes ces méthodes, je n’ai pas touché mes encarts et j’ai pu aller au curseur ou à n’importe où dans le texte que je veux.
vous pouvez utiliser la propriété textContainerInset
de UITextView
:
textView.textContainerInset = UIEdgeInsetsMake (10, 10, 10, 10);
(en haut, à gauche, en bas, à droite)
Pour iOS 10, la ligne suivante fonctionne pour la suppression du remplissage inférieur et supérieur.
captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
Voici une version mise à jour de la réponse très utile de Fattie . Elle ajoute 2 lignes très importantes qui m'ont aidé à obtenir une mise en page parfaite pour iOS 10 et 11 (et probablement aussi pour les versions inférieures).
ré
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
translatesAutoresizingMaskIntoConstraints = true
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
translatesAutoresizingMaskIntoConstraints = false
}
}
Les lignes importantes sont les deux instructions translatesAutoresizingMaskIntoConstraints = <true/false>
!
Ceci surprend supprime toutes les marges dans toutes mes circonstances!
Bien que textView
ne soit pas le premier intervenant, il peut arriver qu’une étrange marge inférieure ne puisse pas être résolue à l’aide de la méthode sizeThatFits
mentionnée dans la réponse acceptée.
Lorsque vous avez tapé dans le textView soudainement, l’étrange marge inférieure a disparu et tout s’est passé comme il se doit, mais seulement dès que le textView est devenu firstResponder
.
J'ai donc lu ici sur SO que l'activation et la désactivation de translatesAutoresizingMaskIntoConstraints
aide lors de la définition manuelle du cadre/des limites entre les appels.
Heureusement, cela ne fonctionne pas uniquement avec les paramètres de cadre, mais avec les 2 lignes de setup()
prises en sandwich entre les deux appels translatesAutoresizingMaskIntoConstraints
!
Ceci est par exemple très utile lors du calcul du cadre d'une vue en utilisant systemLayoutSizeFitting
sur une UIView
. Redonne la bonne taille (ce qui n'était pas le cas auparavant)!
Comme dans la réponse originale mentionnée:
N'oubliez pas de désactiver scrollEnabled dans l'inspecteur!
Cette solution fonctionne correctement dans le storyboard, ainsi qu’au moment de l’exécution.
C'est ça, maintenant tu es vraiment fini!
Pour Swift 4, Xcode 9
Utilisez la fonction suivante peut changer la marge/le remplissage du texte dans UITextView
fonction publique UIEdgeInsetsMake (_ en haut: CGFloat, _ à gauche: CGFloat, _ en bas: CGFloat, _ à droite: CGFloat) -> UIEdgeInsets
alors dans ce cas c'est
self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
Dernières Swift:
self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
Voici une petite extension facile qui supprimera la marge par défaut d’Apple de chaque affichage de texte dans votre application.
Remarque: Interface Builder affichera toujours l'ancienne marge, mais votre application fonctionnera comme prévu.
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);
}
}
En faisant la solution insérée, il me restait du rembourrage sur le côté droit et le bas. De plus, l'alignement du texte posait problème. Le seul moyen sûr que j'ai trouvé était de placer la vue texte dans une autre vue découpée.
Pour moi (iOS 11 et Xcode 9.4.1), ce qui a fonctionné par magie a été de configurer la propriété textView.font sur le style UIFont.preferred(forTextStyle:UIFontTextStyle)
et sur la première réponse mentionnée par @Fattie. Mais la réponse @Fattie n'a pas fonctionné tant que je n'ai pas défini la propriété textView.font, sinon UITextView se comporte de manière erratique.
Si quelqu'un cherche la dernière version de Swift, le code ci-dessous fonctionne correctement avec Xcode 10.2 et Swift 4.2
yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)