iOS7 TextKit: alineación de punto de bala
cocoa-touch uitextview (8)
Así que miré a mi alrededor, y aquí está el código mínimo extraído de la respuesta de Duncan para que funcione:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:yourLabel.text];
NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init];
[paragrahStyle setParagraphSpacing:4];
[paragrahStyle setParagraphSpacingBefore:3];
[paragrahStyle setFirstLineHeadIndent:0.0f]; // First line is the one with bullet point
[paragrahStyle setHeadIndent:10.5f]; // Set the indent for given bullet character and size font
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragrahStyle
range:NSMakeRange(0, [self.descriptionLabel.text length])];
yourLabel.attributedText = attributedString;
Y aquí está el resultado de eso en mi aplicación:
Estoy escribiendo una aplicación solo para iOS 7 y estoy tratando de obtener un formato decente en los puntos de bala en un UITextView no editable.
Es bastante fácil simplemente insertar un carácter de punto de bala, pero, por supuesto, la sangría izquierda no seguirá. ¿Cuál es la forma más fácil en iOS 7 para establecer una sangría izquierda después de un punto de bala?
Gracias por adelantado,
Franco
Basado en la solución de , actualizado a Swift 4.2.
Swift 4.2
let array = ["1st", "2nd", "3rd"]
let textView = UITextView()
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.headIndent = 12
let bulletListText = array.map { "• /($0)" }.joined(separator: "/n")
let attributes = [
NSAttributedString.Key.paragraphStyle: paragraphStyle,
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17.0)
]
textView.attributedText = NSAttributedString(string: bulletListText, attributes: attributes)
Debajo, el código que utilizo para establecer un párrafo con viñetas. Esto viene directamente de una aplicación de trabajo y se usa para aplicar el estilo a todo el párrafo en respuesta a un usuario que hace clic en un botón de formato. He tratado de poner en todos los métodos dependientes, pero puede haber perdido algunos.
Tenga en cuenta que estoy configurando la mayoría de las sangrías en centímetros y, por lo tanto, el uso de las funciones de conversión al final de la lista.
También estoy comprobando la presencia de un carácter de tabulación (¡no hay clave de tabulación en iOS!) E inserto automáticamente un guión y una pestaña.
Si todo lo que necesita es el estilo de párrafo, mire los últimos métodos a continuación donde se configura firstLineIndent, etc.
Tenga en cuenta que todas estas llamadas se envuelven en [textStorage beginEditing/endEditing]
. A pesar de (IBAction) debajo, el método no es llamado directamente por un objeto UI.
- (IBAction) styleBullet1:(id)sender
{
NSRange charRange = [self rangeForUserParagraphAttributeChange];
NSTextStorage *myTextStorage = [self textStorage];
// Check for "-/t" at beginning of string and add if not found
NSAttributedString *attrString = [myTextStorage attributedSubstringFromRange:charRange];
NSString *string = [attrString string];
if ([string rangeOfString:@"/t"].location == NSNotFound) {
NSLog(@"string does not contain tab so insert one");
NSAttributedString * aStr = [[NSAttributedString alloc] initWithString:@"-/t"];
// Insert a bullet and tab
[[self textStorage] insertAttributedString:aStr atIndex:charRange.location];
} else {
NSLog(@"string contains tab");
}
if ([self isEditable] && charRange.location != NSNotFound)
{
[myTextStorage setAttributes:[self bullet1Style] range:charRange];
}
}
- (NSDictionary*)bullet1Style
{
return [self createStyle:[self getBullet1ParagraphStyle] font:[self normalFont] fontColor:[UIColor blackColor] underlineStyle:NSUnderlineStyleNone];
}
- (NSDictionary*)createStyle:(NSParagraphStyle*)paraStyle font:(UIFont*)font fontColor:(UIColor*)color underlineStyle:(int)underlineStyle
{
NSMutableDictionary *style = [[NSMutableDictionary alloc] init];
[style setValue:paraStyle forKey:NSParagraphStyleAttributeName];
[style setValue:font forKey:NSFontAttributeName];
[style setValue:color forKey:NSForegroundColorAttributeName];
[style setValue:[NSNumber numberWithInt: underlineStyle] forKey:NSUnderlineStyleAttributeName];
FLOG(@" font is %@", font);
return style;
}
- (NSParagraphStyle*)getBullet1ParagraphStyle
{
NSMutableParagraphStyle *para;
para = [self getDefaultParagraphStyle];
NSMutableArray *tabs = [[NSMutableArray alloc] init];
[tabs addObject:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:[self ptsFromCMF:1.0] options:nil]];
//[tabs addObject:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:[self ptsFromCMF:1.0]]];
[para setTabStops:tabs];
[para setDefaultTabInterval:[self ptsFromCMF:2.0]];
[para setFirstLineHeadIndent:[self ptsFromCMF:0.0]];
//[para setHeaderLevel:0];
[para setHeadIndent:[self ptsFromCMF:1.0]];
[para setParagraphSpacing:3];
[para setParagraphSpacingBefore:3];
return para;
}
- (NSMutableParagraphStyle*)getDefaultParagraphStyle
{
NSMutableParagraphStyle *para;
para = [[NSParagraphStyle defaultParagraphStyle]mutableCopy];
[para setTabStops:nil];
[para setAlignment:NSTextAlignmentLeft];
[para setBaseWritingDirection:NSWritingDirectionLeftToRight];
[para setDefaultTabInterval:[self ptsFromCMF:3.0]];
[para setFirstLineHeadIndent:0];
//[para setHeaderLevel:0];
[para setHeadIndent:0.0];
[para setHyphenationFactor:0.0];
[para setLineBreakMode:NSLineBreakByWordWrapping];
[para setLineHeightMultiple:1.0];
[para setLineSpacing:0.0];
[para setMaximumLineHeight:0];
[para setMinimumLineHeight:0];
[para setParagraphSpacing:6];
[para setParagraphSpacingBefore:3];
//[para setTabStops:<#(NSArray *)#>];
[para setTailIndent:0.0];
return para;
}
-(NSNumber*)ptsFromCMN:(float)cm
{
return [NSNumber numberWithFloat:[self ptsFromCMF:cm]];
}
-(float)ptsFromCMF:(float)cm
{
return cm * 28.3464567;
}
Esta es la solución más fácil que he encontrado:
let bulletList = UILabel()
let bulletListArray = ["line 1 - enter a bunch of lorem ipsum here so it wraps to the next line", "line 2", "line 3"]
let joiner = "/n"
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.headIndent = 10
paragraphStyle.firstLineHeadIndent = 0
let attributes = [NSParagraphStyleAttributeName: paragraphStyle]
let bulletListString = joiner.join(bulletListArray.map { "• /($0)" })
bulletList.attributedText = NSAttributedString(string: bulletListString, attributes: attributes)
la teoría es que cada cadena de la matriz actúa como un ''párrafo'' y el estilo del párrafo tiene una sangría de 0 en la primera línea, lo que hace que se agregue una viñeta utilizando el método de mapa. Luego, para cada línea después de que tenga una sangría de 10 px (ajuste el espaciado para sus métricas de fuente)
Hice una solución rápida (Swift 2.3 en este momento) basada en la implementación de Lukas. Tuve un pequeño problema con las líneas que no tenían viñetas, así que hice la extensión para que opcionalmente pueda pasar un rango para aplicar el estilo de párrafo.
extension String{
func getAllignedBulletPointsMutableString(bulletPointsRange: NSRange = NSMakeRange(0, 0)) -> NSMutableAttributedString{
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: self)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.paragraphSpacing = 0
paragraphStyle.paragraphSpacingBefore = 0
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.headIndent = 7.5
attributedString.addAttributes([NSParagraphStyleAttributeName: paragraphStyle], range: bulletPointsRange)
return attributedString
}
}
Otras respuestas se basan en establecer el tamaño de sangría con un valor constante. Eso significa que tendrá que actualizarlo manualmente si está cambiando las fuentes, y no funcionará bien si está usando Dynamic Type. Afortunadamente, medir texto es fácil.
Digamos que tiene algún texto y algunos atributos:
NSString *text = @"• Some bulleted paragraph";
UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
NSDictionary *attributes = @{NSFontAttributeName: font};
Aquí le indicamos cómo medir la viñeta y crear un estilo de párrafo en consecuencia:
NSString *bulletPrefix = @"• ";
CGSize size = [bulletPrefix sizeWithAttributes:attributes];
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.headIndent = size.width;
Insertamos esto en nuestros atributos y creamos la cadena atribuida:
NSMutableDictionary *indentedAttributes = [attributes mutableCopy];
indentedAttributes[NSParagraphStyleAttributeName] = [paragraphStyle copy];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:text attributes:indentedAttributes];
Swift 4
Hice una extensión para NSAttributedString
que agrega un inicializador de conveniencia que NSAttributedString
adecuadamente los diferentes tipos de listas.
extension NSAttributedString {
convenience init(listString string: String, withFont font: UIFont) {
self.init(attributedListString: NSAttributedString(string: string), withFont: font)
}
convenience init(attributedListString attributedString: NSAttributedString, withFont font: UIFont) {
guard let regex = try? NSRegularExpression(pattern: "^(//d+//.|[•//-//*])(//s+).+$",
options: [.anchorsMatchLines]) else { fatalError() }
let matches = regex.matches(in: attributedString.string, options: [],
range: NSRange(location: 0, length: attributedString.string.utf16.count))
let nsString = attributedString.string as NSString
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
for match in matches {
let size = NSAttributedString(
string: nsString.substring(with: match.range(at: 1)) + nsString.substring(with: match.range(at: 2)),
attributes: [.font: font]).size()
let indentation = ceil(size.width)
let range = match.range(at: 0)
let paragraphStyle = NSMutableParagraphStyle()
if let style = attributedString.attribute(.paragraphStyle, at: 0, longestEffectiveRange: nil, in: range)
as? NSParagraphStyle {
paragraphStyle.setParagraphStyle(style)
}
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: [:])]
paragraphStyle.defaultTabInterval = indentation
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.headIndent = indentation
mutableAttributedString.addAttribute(.font, value: font, range: range)
mutableAttributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: range)
}
self.init(attributedString: mutableAttributedString)
}
}
El número de espacios después de cada bala, etc. no importa. El código calculará el ancho de sangría apropiado de forma dinámica según la cantidad de pestañas o espacios que decida tener después de la viñeta.
Si la cadena atribuida ya tiene un estilo de párrafo, el inicializador de conveniencia conservará las opciones de ese estilo de párrafo y aplicará algunas opciones propias.
Símbolos admitidos: •, -, *, números seguidos de un punto (por ejemplo, 8.)