web-dev-qa-db-fra.com

Comment perdre de la marge/du remplissage dans UITextView?

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!

425
sniurkst

Correctement à jour pour 2019

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!

  1. La solution fonctionne correctement dans le storyboard

  2. La solution fonctionne correctement à l'exécution

Ça y est, vous avez terminé.

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 ...

screenshot of IB with UITextView

Voici UITextViewFixed:

screenshot of IB with UITextViewFixed

Notez que vous devez bien sûr

désactivez scrollEnabled dans l'inspecteur!

N'oubliez pas de désactiver scrollEnabled! :)


Quelques questions supplémentaires

(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

248
Fattie

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.

751
user1687195

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.

250
Michael

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+:

enter image description here

68
azdev

Sur iOS 5, UIEdgeInsetsMake(-8,-8,-8,-8); semble bien fonctionner.

44
Engin Kurutepe

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.

39
ldoogy

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}} enter image description hereenter image description here

18
Uladzimir

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.

Taille du contenu du texte

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)

Hauteur de la ligne

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;

Calculer la taille du texte visible

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;

Détermination de la taille de la pagination

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.

15
mikeho

vous pouvez utiliser la propriété textContainerInset de UITextView:

textView.textContainerInset = UIEdgeInsetsMake (10, 10, 10, 10);

(en haut, à gauche, en bas, à droite)

11
Himanshu padia

Pour iOS 10, la ligne suivante fonctionne pour la suppression du remplissage inférieur et supérieur.

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
8
Anson Yao

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).

@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!

6
Fab1n

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)
3
Shan Ye

Dernières Swift:

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
3
Urvish Modi

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);
   }
}
2
Cal S

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. 

2
rp90

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.

0
Nikhil Pandey

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)
0
Bhavin_m