Search code examples
iosswiftobjective-cuiimagecgimage

Add additional information to a UIImage


I'm trying to add some information to a UIImage I am doing some experimentation with to add details like the number of UILabel's present on the screen and their frames.

I tried using setValue:forKeyPath: but this does not work since UIImage is not key-value compliant. What is an efficient way to do this?

Adding metadata using the CGImageMetadata API's seems to need me to convert the UIImage to NSData. Also, I would like to access the data without saving it every time. Can anyone point me to a good way to do this?


Solution

  • You can get use of associated objects to complement objects of classes source of which you don't have access to with some additional data (with use of objc_setAssociatedObject to set and objc_getAssociatedObject to read the data):

    UIImage *image = [UIImage new];
    NSString *extraData = @"extra info";
    
    objc_setAssociatedObject(image, @"example", extraData, OBJC_ASSOCIATION_COPY_NONATOMIC);
    NSLog(@"%@", objc_getAssociatedObject(image, @"example"));
    

    For convenience it can be effectively wrapped with a property by extending the class with a category:

    //  UIImage+TDWExtraData.h
    
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface UIImage (TDWExtraData)
    
    @property (nonatomic, copy) NSString *tdw_extraData;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    //  UIImage+TDWExtraData.m
    
    #import "UIImage+TDWExtraData.h"
    #import <objc/runtime.h>
    
    static void const *const kExtraDataKey = &kExtraDataKey;
    
    @implementation UIImage (TDWExtraData)
    
    - (NSString *)tdw_extraData {
        return objc_getAssociatedObject(self, kExtraDataKey);
    }
    
    - (void)setTdw_extraData:(NSString *)data {
        objc_setAssociatedObject(self, kExtraDataKey, data, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    
    @end
    

    P.S. It's not correct to say that UIImage is not KVC compliant only because it doesn't support setValue:forKeyPath: for non-existing key paths. It's perfectly compliant for key-paths the class has defined:

    UIImage *obj = [UIImage new];
    NSLog(@"%@", [obj valueForKey:@"size"]); // prints size