ios - ¿Cómo hacer que la altura de UITextView sea dinámica según la longitud del texto?
swift (17)
1 Agregar un observador a la longitud del contenido del campo de texto
yourTextView.addObserver(self, forKeyPath: "contentSize", options: (NSKeyValueObservingOptions.new), context: nil);
2 Implementar observador
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
let tv = object as! UITextView;
var topCorrect = (tv.bounds.size.height - tv.contentSize.height * tv.zoomScale)/2.0;
topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
tv.contentOffset.x = 0;
tv.contentOffset.y = -topCorrect;
self.yourTextView.contentSize.height = tv.contentSize.height;
UIView.animate(withDuration: 0.2, animations: {
self.view.layoutIfNeeded();
});
}
Aquí hay dos trampas en iOS 8.3 cuando viene con
textView.textContainer.maximumNumberOfLines = 10
Consulte mi gist , por favor.
textView.attributedText = originalContent
let lineLimit = 10
textView.isEditable = true
textView.isScrollEnabled = false
textView.textContainerInset = .zero // default is (8, 0, 8, 0)
textView.textContainer.maximumNumberOfLines = lineLimit // Important condition
textView.textContainer.lineBreakMode = .byTruncatingTail
// two incomplete methods, which do NOT work in iOS 8.3
// size.width可能比maxSize.width小 ————遗憾的是 iOS 8.3 上此方法无视maximumNumberOfLines参数,所以得借助于UILabel
// size.width may be less than maxSize.width, ---- Do NOT work in iOS 8.3, which disregards textView.textContainer.maximumNumberOfLines
// let size = textView.sizeThatFits(maxSize)
// 遗憾的是 iOS 8.3 上此方法失效了,得借助于UILabel
// Does not work in iOS 8.3
// let size = textView.layoutManager.usedRectForTextContainer(textView.textContainer).size
// Suggested method: use a temperary label to get its size
let label = UILabel(); label.attributedText = originalContent
let size = label.textRect(forBounds: CGRect(origin: .zero, size: maxSize), limitedToNumberOfLines: lineLimit).size
textView.frame.size = size
En Storyboard / Interface Builder, simplemente desactive el desplazamiento en el inspector de atributos.
En el código
textField.isScrollEnabled = false
debería hacer el truco.
En mi proyecto, el controlador de vista está involucrado con muchas Restricciones y StackView, y configuré la altura de TextView como una restricción, y varía según el valor textView.contentSize.height.
Paso 1: obtenga una salida IB
@IBOutlet weak var textViewHeight: NSLayoutConstraint!
Paso 2: utilice el método de delegación a continuación.
extension NewPostViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
textViewHeight.constant = self.textView.contentSize.height + 10
}
}
Es sencillo hacerlo de forma programática. solo sigue estos pasos
-
agregar un observador a la longitud del contenido del campo de texto
[yourTextViewObject addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
-
implementar observador
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { UITextView *tv = object; //Center vertical alignment CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect}; mTextViewHeightConstraint.constant = tv.contentSize.height; [UIView animateWithDuration:0.2 animations:^{ [self.view layoutIfNeeded]; }]; }
-
si desea detener textviewHeight para aumentar después de un tiempo durante la escritura, implemente esto y establezca textview delegate en self.
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { if(range.length + range.location > textView.text.length) { return NO; } NSUInteger newLength = [textView.text length] + [text length] - range.length; return (newLength > 100) ? NO : YES; }
Esta respuesta puede llegar tarde, pero espero que ayude a alguien.
Para mí, estas 2 líneas de código funcionaron:
textView.isScrollEnabled = false
textView.sizeToFit()
Pero no establezca restricciones de altura para su vista de texto
Esto funciona para mí, todas las demás soluciones no lo hicieron.
func adjustUITextViewHeight(arg : UITextView)
{
arg.translatesAutoresizingMaskIntoConstraints = true
arg.sizeToFit()
arg.scrollEnabled = false
}
En Swift 4, la sintaxis de
arg.scrollEnabled = false
ha cambiado a
arg.isScrollEnabled = false
.
Mejor aún, rápido 4 agregar como una extensión:
extension UITextView {
func resizeForHeight(){
self.translatesAutoresizingMaskIntoConstraints = true
self.sizeToFit()
self.isScrollEnabled = false
}
}
Prueba esto:
CGRect frame = self.textView.frame;
frame.size.height = self.textView.contentSize.height;
self.textView.frame = frame;
Editar: aquí está el Swift:
var frame = self.textView.frame
frame.size.height = self.textView.contentSize.height
self.textView.frame = frame
Puedes hacer eso con IB.
-
Herede su UIViewController de UITextViewDelegate
-
Entonces
``
func textViewDidChange(_ textView: UITextView) {
// calculate text height
let constraintRect = CGSize(width: textView.frame.width,
height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect,
options: .usesLineFragmentOrigin,
attributes: [.font: textView.font],
context: nil)
let height = ceil(boundingBox.height)
// textViewHeightConstraint - your height constraint outlet from IB
if height > textViewHeightConstraint.constant {
textViewHeightConstraint.constant = height
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
})
}
}`
SWIFT 4
Cambiar el tamaño al escribir
UITextViewDelegate
func textViewDidChange(_ textView: UITextView) {
yourTextView.translatesAutoresizingMaskIntoConstraints = true
yourTextView.sizeToFit()
yourTextView.isScrollEnabled = false
let calHeight = yourTextView.frame.size.height
yourTextView.frame = CGRect(x: 16, y: 193, width: self.view.frame.size.width - 32, height: calHeight)
}
Cambiar el tamaño cuando carga
func textViewNotasChange(arg : UITextView) {
arg.translatesAutoresizingMaskIntoConstraints = true
arg.sizeToFit()
arg.isScrollEnabled = false
let calHeight = arg.frame.size.height
arg.frame = CGRect(x: 16, y: 40, width: self.view.frame.size.width - 32, height: calHeight)
}
Llame a la función de la segunda opción de esta manera:
textViewNotasChange(arg: yourTextView)
Seguido por la respuesta de DeyaEldeen.
En mi caso.
Aumento la altura de la vista de texto automáticamente agregando
rápido 3
textView.translatesAutoresizingMaskIntoConstraints = false textView.isScrollEnabled = false
Si su
textView
puede crecer tanto como el contenido, entonces
textView.isScrollEnabled = false
debería funcionar con autolayout.
Si desea que
textView
permanezca desplazable, debe agregar una restricción de altura opcional,
internal lazy var textViewHeightConstraint: NSLayoutConstraint = {
let constraint = self.textView.heightAnchor.constraint(equalToConstant: 0)
constraint.priority = .defaultHigh
return constraint
}()
public override func layoutSubviews() {
super.layoutSubviews()
// Assuming there is width constraint setup on the textView.
let targetSize = CGSize(width: textView.frame.width, height: CGFloat(MAXFLOAT))
textViewHeightConstraint.constant = textView.sizeThatFits(targetSize).height
}
La razón para anular
layoutSubviews()
es asegurarse de que textView se presenta correctamente horizontalmente para que podamos confiar en el ancho para calcular la altura.
Dado que la restricción de altura se establece en una prioridad inferior, si se agota el espacio verticalmente, la altura real de
textView
será menor que
contentSize
.
Y el textView será desplazable.
esta funcionando
func textViewDidChange(_ textView: UITextView) {
let fixedWidth = textviewconclusion.frame.size.width
textviewconclusion.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
let newSize = textviewconclusion.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
var newFrame = textviewconclusion.frame
newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
textviewconclusion.frame = newFrame
}
simplemente haga una conexión con la restricción de altura de textView
@IBOutlet var textView: UITextView!
@IBOutlet var textViewHeightConstraint: NSLayoutConstraint!
y usa este código a continuación
textViewHeightConstraint.constant = self.textView.contentSize.height
Swift 4
Añádelo a tu clase
UITextViewDelegate
func textViewDidChange(_ textView: UITextView) {
let fixedWidth = textView.frame.size.width
textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
var newFrame = textView.frame
newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
textView.frame = newFrame
}
Swift 4+
¡Esto es extremadamente fácil con
autolayout
!
Explicaré el caso de uso más simple.
Digamos que solo hay un
UITextView
en su
UITableViewCell
.
-
Ajuste el
textView
alcontentView
con restricciones. -
Deshabilite el desplazamiento para
textView
. -
Actualice
tableView
entextViewDidChange
.
(Puede realizar el último paso de dos maneras: pase la instancia de tableView a la celda o configure el delegado textView en su controlador de vista e implemente el delegado
textViewDidChange
allí y actualice la vista de tabla).
¡Eso es todo!
class TextViewCell: UITableViewCell {
//MARK: UI Element(s)
/// Reference of the parent table view so that it can be updated
var tableView: UITableView!
lazy var textView: UITextView = {
let textView = UITextView()
textView.isScrollEnabled = false
// Other textView properties
return textView
}()
//MARK: Padding Variable(s)
let padding: CGFloat = 50
//MARK: Initializer(s)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubviews()
addConstraints()
textView.becomeFirstResponder()
}
//MARK: Helper Method(s)
func addSubviews() {
contentView.addSubview(textView)
}
func addConstraints() {
textView.leadingAnchor .constraint(equalTo: contentView.leadingAnchor, constant: padding).isActive = true
textView.trailingAnchor .constraint(equalTo: contentView.trailingAnchor, constant: -padding).isActive = true
textView.topAnchor .constraint(equalTo: contentView.topAnchor, constant: padding).isActive = true
textView.bottomAnchor .constraint(equalTo: contentView.bottomAnchor, constant: -padding).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension TextViewCell: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
Mira mi repo para la implementación completa.