Pregunta

Necesito agregar un atributo personalizado al texto seleccionado en un nstextView. Así que puedo hacerlo obteniendo la cadena atribuida para la selección, agregando un atributo personalizado y luego reemplazando la selección con mi nueva cadena atribuida.

Así que ahora obtengo la cadena atribuida de la vista de texto como NSDATA y la escribo en un archivo. Más tarde, cuando abro ese archivo y lo restauro a la vista de texto, ¡mis atributos personalizados se han ido! Después de resolver todo el esquema para mi atributo personalizado, encuentro que los atributos personalizados no se guardan para usted. Mira la nota importante aquí: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/attributedstrings/tasks/rtfandattrstrings.html

Así que no tengo idea de cómo guardar y restaurar mis documentos con este atributo personalizado. ¿Alguna ayuda?

¿Fue útil?

Solución

La forma normal de salvar un NSAttributedString es usar RTF, y los datos de RTF son lo que el -dataFromRange:documentAttributes:error: método de NSAttributedString genera.

Sin embargo, el formato RTF no tiene soporte para atributos personalizados. En su lugar, debe usar el NSCoding Protocolo para archivar su cadena atribuida, que preservará los atributos personalizados:

//asssume attributedString is your NSAttributedString
//encode the string as NSData
NSData* stringData = [NSKeyedArchiver archivedDataWithRootObject:attributedString];
[stringData writeToFile:pathToFile atomically:YES];

//read the data back in and decode the string
NSData* newStringData = [NSData dataWithContentsOfFile:pathToFile];
NSAttributedString* newString = [NSKeyedUnarchiver unarchiveObjectWithData:newStringData];

Otros consejos

Hay una manera de ahorrar atributos personalizados a RTF usando cacao. Se basa en el hecho de que RTF es un formato de texto, por lo que puede manipularse como una cadena incluso si no conoce todas las reglas de RTF y no tiene un lector/escritor de RTF personalizado. El procedimiento que describe a continuación postprocesos el RTF tanto al escribir y leer, y he utilizado esta técnica personalmente. Una cosa de la que tener mucho cuidado es que el texto que inserta en el RTF usa solo 7 bits ASCII y no caracteres de control sin escaparate, que incluyen " {}".

Así es como codificaría sus datos:

NSData *GetRtfFromAttributedString(NSAttributedString *text)
{
    NSData *rtfData = nil;
    NSMutableString *rtfString = nil;
    NSString *customData = nil, *encodedData = nil;
    NSRange range;
    NSUInteger dataLocation;

// Convert the attributed string to RTF
    if ((rtfData = [text RTFFromRange:NSMakeRange(0, [text length]) documentAttributes:nil]) == nil)
        return(nil);

// Find and encode your custom attributes here. In this example the data is a string and there's at most one of them
    if ((customData = [text attribute:@"MyCustomData" atIndex:0 effectiveRange:&range]) == nil)
        return(rtfData); // No custom data, return RTF as is
    dataLocation = range.location;

// Get a string representation of the RTF
    rtfString = [[NSMutableString alloc] initWithData:rtfData encoding:NSASCIIStringEncoding];

// Find the anchor where we'll put our data, namely just before the first paragraph property reset
    range = [rtfString rangeOfString:@"\\pard" options:NSLiteralSearch];
    if (range.location == NSNotFound)
        {
        NSLog(@"Custom data dropped; RTF has no paragraph properties");
        [rtfString release];
        return(rtfData);
        }

// Insert the starred group containing the custom data and its location
    encodedData = [NSString stringWithFormat:@"{\\*\\my_custom_keyword %d,%@}\n", dataLocation, customData];
    [rtfString insertString:encodedData atIndex:range.location];

// Convert the amended RTF back to a data object    
    rtfData = [rtfString dataUsingEncoding:NSASCIIStringEncoding];
    [rtfString release];
    return(rtfData);
}

Esta técnica funciona porque todos los lectores de RTF que cumplen ignorarán "grupos protagonizados" cuya palabra clave no reconocen. Por lo tanto, desea asegurarse de que su palabra de control no sea reconocida por ningún otro lector, por lo que use algo que sea único, como un prefijo con su empresa o nombre del producto. Si sus datos son complejos, o binarios, o pueden contener caracteres RTF ilegales que no desea escapar, codifíquelo en Base64. Asegúrese de poner un espacio después de su palabra clave.

Del mismo modo, al leer el RTF, busca su palabra de control, extrae los datos y restaura el atributo. Esta rutina toma como argumentos la cadena atribuida y el RTF desde el que fue creado.

void RestoreCustomAttributes(NSMutableAttributedString *text, NSData *rtfData)
{
    NSString *rtfString = [[NSString alloc] initWithData:rtfData encoding:NSASCIIStringEncoding];
    NSArray *components = nil;
    NSRange range, endRange;

// Find the custom data and its end
    range = [rtfString rangeOfString:@"{\\*\\my_custom_keyword " options:NSLiteralSearch];
    if (range.location == NSNotFound)
        {
        [rtfString release];
        return;
        }
    range.location += range.length;

    endRange = [rtfString rangeOfString:@"}" options:NSLiteralSearch
        range:NSMakeRange(range.location, [rtfString length] - endRange.location)];
    if (endRange.location == NSNotFound)
        {
        [rtfString release];
        return;
        }

// Get the location and the string data, which are separated by a comma
    range.length = endRange.location - range.location;
    components = [[rtfString substringWithRange:range] componentsSeparatedByString:@","];
    [rtfString release];

// Assign the custom data back to the attributed string. You should do range checking here (omitted for clarity)
    [text addAttribute:@"MyCustomData" value:[components objectAtIndex:1]
        range:NSMakeRange([[components objectAtIndex:0] integerValue], 1)];
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top