iphone - teclados - ¿Vista como clave del diccionario?
teclados para iphone 6 (7)
Quiero tener un NSDictionary
que mapea de UIView
s a otra cosa.
Sin embargo, como UIViews no implementa el protocolo NSCopying
, no puedo usarlos directamente como claves de diccionario.
Aquí está el código real (basado en la respuesta de luvieere y otra sugerencia de Yar):
// create dictionary
NSMutableDictionary* dict = [NSMutableDictionary new];
// set value
UIView* view = [UILabel new];
dict[[NSValue valueWithNonretainedObject:view]] = @"foo";
// get value
NSString* foo = dict[[NSValue valueWithNonretainedObject:view]];
Aunque esto no es realmente para lo que están destinados, usted podría crear una interfaz funcional similar a un diccionario utilizando Referencias Asociativas :
static char associate_key;
void setValueForUIView(UIView * view, id val){
objc_setAssociatedObject(view, &associate_key, val, OBJC_ASSOCIATION_RETAIN);
}
id valueForUIView(UIView * view){
return objc_getAssociatedObject(view, &associate_key);
}
Incluso podrías envolver esto en una clase ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable
*; en ese caso, es posible que desee conservar las vistas que utiliza como claves.
Algo como esto (sin probar):
#import "ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable.h"
#import <objc/runtime.h>
static char associate_key;
@implementation ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable
- (void)setObject: (id)obj forKey: (id)key
{
// Remove association and release key if obj is nil but something was
// previously set
if( !obj ){
if( [self objectForKey:key] ){
objc_setAssociatedObject(key, &associate_key, nil, OBJC_ASSOCIATION_RETAIN);
[key release];
}
return;
}
[key retain];
// retain/release for obj is handled by associated objects functions
objc_setAssociatedObject(key, &associate_key, obj, OBJC_ASSOCIATION_RETAIN);
}
- (id)objectForKey: (id)key
{
return objc_getAssociatedObject(key, &associate_key);
}
@end
* El nombre puede necesitar algún trabajo.
En lugar de almacenar un puntero a la vista y arriesgar el problema de la basura, simplemente asigne una etiqueta a UIView y almacene el valor de la etiqueta en el diccionario. Mucho más seguro.
Estoy usando una solución simple bajo ARC proporcionada por Objective-C ++.
MyClass.mm:
#import <map>
@implementation MyClass
{
std::map<UIView* __weak, UIColor* __strong> viewMap;
}
- (void) someMethod
{
viewMap[self.someView] = [UIColor redColor];
}
En este ejemplo, estoy obteniendo una verificación de tipos más fuerte al hacer que todos los valores tengan que ser un UIColor*
que es todo lo que necesitaba para esto. Pero también puede usar id
como el tipo de valor si desea permitir cualquier objeto como el valor, ej: std::map<UIView* __weak, id __strong> viewMap;
Del mismo modo para las teclas: id __weak, id __strong> viewMap;
También puede variar los __strong
y __weak
según sea necesario. En mi caso, las vistas ya están retenidas por el controlador de vista en el que uso esto, por lo que no vi la necesidad de apuntar con fuerza.
Puede usar un NSValue
sosteniendo el puntero a UIView
y usar esto como clave. NSValues
son copiables. pero, si se destruye la vista, NSValue
mantendrá un puntero no NSValue
.
Siempre que no necesite soporte antes de iOS 6, NSMapTable (sugerido por nielsbot ) funciona bien porque puede proporcionar un enumerador sobre las claves de la colección. Eso es útil para el código común a todos los campos de texto, como configurar el delegado o sincronizar bidireccionalmente los valores de texto con una instancia de NSUserDefaults.
en viewDidLoad
self.userDefFromTextField = [NSMapTable weakToStrongObjectsMapTable];
[self.userDefFromTextField setObject:@"fooUserDefKey" forKey:self.textFieldFoo];
[self.userDefFromTextField setObject:@"barUserDefKey" forKey:self.textFieldBar];
// skipped for clarity: more text fields
NSEnumerator *textFieldEnumerator = [self.userDefFromTextField keyEnumerator];
UITextField *textField;
while (textField = [textFieldEnumerator nextObject]) {
textField.delegate = self;
}
A la vista, aparecerá:
NSEnumerator *keyEnumerator = [self.userDefFromTextField keyEnumerator];
UITextField *textField;
while (textField = [keyEnumerator nextObject]) {
textField.text = [self.userDefaults stringForKey:[self.textFields objectForKey:textField]];
}
en textField: shouldChangeCharactersInRange: replacementString:
NSString *resultingText = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(resultingText.length == 0) return YES;
NSString *preferenceKey = [self.textFields objectForKey:textField];
if(preferenceKey) [self.userDefaults setString:resultingText forKey:preferenceKey];
return YES;
Y ahora voy a llorar, porque implementé todo esto antes de darme cuenta de que mi aplicación orientada a iOS 5.1 no puede usarla. NSMapTable fue introducido en iOS 6.
una solución simple cuando solo desea UIView
como clave de vez en cuando, lo uso para almacenar UILabel
y UIColor
NSArray<UIView *> *views = @[viewA,viewB,viewC,viewD];
NSArray *values = @[valueA,valueB,valueC,valueD];
for(int i = 0;i < 4;i++) {
UIView *key = views[i];
id value = values[i]
//do something
}
id value = values[[views indexOfObject:key]]