Je me demandais comment limiter la quantité de LIGNES (pas de caractères comme demandé dans d'autres questions) qu'un utilisateur peut entrer lors de la modification d'un UITextField.
Idéalement, je voudrais limiter l'entrée à max. 10 lignes.
Par où devrais-je commencer? Dois-je le faire avec une méthode? Dans
- (BOOL)textViewShouldBeginEditing:(UITextView *)aTextView
Vous avez la bonne idée, mais la mauvaise méthode. textView:shouldChangeTextInRange:replacementText:
est appelé chaque fois que le texte va changer; vous pouvez accéder au contenu actuel de la vue texte à l'aide de sa propriété text
, et vous pouvez construire le nouveau contenu à partir de la plage passée et du texte de remplacement avec [textView.text stringByReplacingCharactersInRange:range withString:replacementText]
. Vous pouvez ensuite compter le nombre de lignes et retourner OUI pour autoriser la modification ou NON pour la rejeter.
La réponse de Maciek Czarnik ne fonctionne pas pour moi, mais elle m'a permis de savoir quoi faire.
iOS 7+
Swift
textView.textContainer.maximumNumberOfLines = 10
textView.textContainer.lineBreakMode = .byTruncatingTail
ObjC
textView.textContainer.maximumNumberOfLines = 10;
textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
Cela peut peut-être aider (iOS 7+):
textView.textContainer.maximumNumberOfLines = 10;
[textView.layoutManager textContainerChangedGeometry:textView.textContainer];
Même la première ligne devrait faire l'affaire, je suppose, mais ce n'est pas le cas ... Peut-être que c'est un bogue dans le SDK
La réponse de Maciek Czarnik ne semble pas fonctionner pour moi, même dans iOS7. Cela me donne un comportement étrange, je ne sais pas pourquoi.
Ce que je fais pour limiter le nombre de lignes dans UITextView est simplement:
(testé uniquement dans iOS7) Dans la méthode UITextViewDelegate suivante:
- (void)textViewDidChange:(UITextView *)textView
{
NSUInteger maxNumberOfLines = 5;
NSUInteger numLines = textView.contentSize.height/textView.font.lineHeight;
if (numLines > maxNumberOfLines)
{
textView.text = [textView.text substringToIndex:textView.text.length - 1];
}
}
dans Swift 3.0
version:
self.textView.textContainer.maximumNumberOfLines = self.textViewNumberOflines
self.textView.textContainer.lineBreakMode = .byTruncatingTail
Voici une version améliorée de la réponse Numereyes dans Swift 4.2/Swift 5
J'ai fait une petite extension pour pouvoir réutiliser le code. J'utilise une boucle While pour vérifier si la taille convient. Cela fonctionne également lorsque l'utilisateur colle beaucoup de texte à la fois.
extension UITextView {
var numberOfCurrentlyDisplayedLines: Int {
let size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
//for Swift <=4.0, replace with next line:
//let size = systemLayoutSizeFitting(UILayoutFittingCompressedSize)
return Int(((size.height - layoutMargins.top - layoutMargins.bottom) / font!.lineHeight))
}
/// Removes last characters until the given max. number of lines is reached
func removeTextUntilSatisfying(maxNumberOfLines: Int) {
while numberOfCurrentlyDisplayedLines > (maxNumberOfLines) {
text = String(text.dropLast())
layoutIfNeeded()
}
}
}
// Use it in UITextView's delegate method:
func textViewDidChange(_ textView: UITextView) {
textView.removeTextUntilSatisfying(maxNumberOfLines: 10)
}
Les autres solutions proposées ne résolvent pas un problème lié à la création d'une dernière ligne à la fin (une 11ème ligne dans le cas de la question).
Voici une solution de travail avec Swift 4.0
& Xcode 9.0 beta (trouvé sur cet article de blog )
class ViewController: UIViewController, UITextViewDelegate {
@IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
textView.textContainer.maximumNumberOfLines = 10
textView.textContainer.lineBreakMode = .byWordWrapping
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
let newLines = text.components(separatedBy: CharacterSet.newlines)
let linesAfterChange = existingLines.count + newLines.count - 1
return linesAfterChange <= textView.textContainer.maximumNumberOfLines
}
Nota bene: Cette solution ne gère pas le scénario où la dernière ligne est trop longue à afficher (le texte sera masqué à l'extrême droite de l'UITextView ).
Similaire à d'autres réponses, mais utilisable directement à partir de Storyboard et sans sous-classement:
extension UITextView {
@IBInspectable var maxNumberOfLines: NSInteger {
set {
textContainer.maximumNumberOfLines = maxNumberOfLines
}
get {
return textContainer.maximumNumberOfLines
}
}
@IBInspectable var lineBreakByTruncatingTail: Bool {
set {
if lineBreakByTruncatingTail {
textContainer.lineBreakMode = .byTruncatingTail
}
}
get {
return textContainer.lineBreakMode == .byTruncatingTail
}
}
}
pour Swift 4 celui-ci a fonctionné sans aucun bogue:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
let newLines = text.components(separatedBy: CharacterSet.newlines)
let linesAfterChange = existingLines.count + newLines.count - 1
return linesAfterChange <= textView.textContainer.maximumNumberOfLines
}
et si vous voulez aussi limiter les caractères:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
let newLines = text.components(separatedBy: CharacterSet.newlines)
let linesAfterChange = existingLines.count + newLines.count - 1
if(text == "\n") {
return linesAfterChange <= textView.textContainer.maximumNumberOfLines
}
let newText = (textView.text as NSString).replacingCharacters(in: range, with: text)
let numberOfChars = newText.count
return numberOfChars <= 30 // 30 characters limit
}
}
n'oubliez pas d'ajouter le nombre de lignes dont vous voulez que la limite soit en viewDidLoad
:
txtView.textContainer.maximumNumberOfLines = 2