Store additional associated infomation in a UIView
Asked Answered
M

4

8

I've been reading up a lot about Gesture Recognizers on SO - and have managed to write a working code which when a long-press is recognised on an UIImage, an action sheet appears:

{ ...
 UILongPressGestureRecognizer *longPressWall = [[[UILongPressGestureRecognizer alloc]
                                                               initWithTarget:self     action:@selector(deleteImage:)] autorelease];
                     longPressWall.minimumPressDuration = 0.4;
                     l.userInteractionEnabled=YES;
                     [l addGestureRecognizer:longPressWall];
... }


-(void)deleteImage:(UILongPressGestureRecognizer*)sender { 
    if(UIGestureRecognizerStateBegan == sender.state) {
        UIActionSheet *as = [[UIActionSheet alloc] initWithTitle:@"" delegate:self cancelButtonTitle:@"Close" destructiveButtonTitle:@"Delete Screenshot" otherButtonTitles: nil];
        [as showInView:masterView];
        [as release];
    }
}

So, sending information to the Selector deleteImage: is a little tricky in this situation. I want to send a HTTP request to a server when deleteImage is called, so I need some information from the view.

Is there anyway to store information into the UIImageView and retrieve it from sender.view.myinfo (for example)?

Millinery answered 21/7, 2012 at 17:59 Comment(4)
Maybe you can use imageView.tag to store some info with number.Leucoma
In the MVC world, you generally don't want to "store information" in a view. If you want to keep track of something like this, you'd probably keep track of it in your controller or, better, the model that your controller uses. So maybe you have some NSMutableArray to keep track of deleted items, and as you delete, remove the image from the view (or possibly dim it if you don't want to remove it until you get confirmation from the server) and then have your controller add the deleted item in the array that you're using to keep track of this stuff. Or something like that.Rodenticide
While it is true that you shouldn't store data in the views, it is very natural to hang pointers to data on them as @nielsbot describes. The use of objc_setAssociatedObject in this case would not be a violation of MVC, as long as the associated object really is just a pointer to the "associated object" (which is part of the model).Ammonium
@RobNapier Agreed. It just gives me shudders when I see people misusing this, storing all sorts of model data in their views, oblivious of all sorts of possible risks.Rodenticide
D
5

The obvious way is to use the tag property. If you need more info you can always subclass the UIImageView and add an extra property.

Dislocate answered 21/7, 2012 at 18:11 Comment(2)
Tag takes nint, storing the item ID in it worked for me as wellHickie
Can we achieve this without subclassing? May be using Swizzle or Category, but in a more efficient manner? I want to have a centralized place for this and I really don't want to replace original class with the subclass created.Rosalia
B
20

Via an extension you can add a property to UIView to store your associated values, like this:

import Foundation
import ObjectiveC

extension UIImageView
{
    struct Static {
        static var key = "key"
    }
    var myInfo:AnyObject? {
        get { 
            return objc_getAssociatedObject( self, &Static.key ) as AnyObject? 
        }
        set { 
            objc_setAssociatedObject( self, &Static.key,  newValue, .OBJC_ASSOCIATION_RETAIN) 
        }
    }
}

Now you can do this anywhere in your code

let anImageView = UIView()

// set your new property on any UIView:
anImageView.myInfo = <some object>

// get your proeprty from any UIView
myImage = anImageView.myInfo

previous answer (same code, but in Objective-C) Check out objc_setAssociatedObject() in <objc/runtime.h>

I would implement this as a category.. (ARC-style)

#import <objc/runtime.h>

@interface UIImageView (MyInfo)
@property ( nonatomic, strong ) id myInfo ;
@end

@implementation UIImageView (MyInfo)

-(void)setMyInfo:(id)info
{
    objc_setAssociatedObject( self, "_myInfo", info, OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}

-(id)myInfo
{
   return objc_getAssociatedObject( self, "_myInfo" ) ;
}

@end

Now you can do this:

UIImage * myImage ;
myImage.myInfo = <some object>
Bismuth answered 21/7, 2012 at 18:6 Comment(8)
I should add--if there's another way to do this, you might try that first. For example, could you use a completion block with your network request? That allows you to capture local variables at the time the block is created, and use them when your request completes..Bismuth
+1 - Although this is amazing (I didn't know you could do this at all), But I'm going to have to award Diederik's answer, as it was super simple to implement. Thanks.Millinery
Up to you--although I always favor composition (categories) vs subclassing. It's easier to avoid painting yourself into a corner.Bismuth
nielsbot, this works if I #import <objc/runtime.h>. I’ll no longer need to use tags. Great! +1Multiplier
That's at the top.. although not expressed as an #import statement.. I could change it I guess...Bismuth
I added that to be more explicitBismuth
nielsbot, I have posted a related question which I think you'd be able to answer if you can spare the time. https://mcmap.net/q/1174343/-if-this-is-the-right-way-to-use-a-customised-string-property-in-objective-c-why-can-t-i-extract-the-correct-numeric-value/2348597Multiplier
@Multiplier Seems answered :)Bismuth
D
5

The obvious way is to use the tag property. If you need more info you can always subclass the UIImageView and add an extra property.

Dislocate answered 21/7, 2012 at 18:11 Comment(2)
Tag takes nint, storing the item ID in it worked for me as wellHickie
Can we achieve this without subclassing? May be using Swizzle or Category, but in a more efficient manner? I want to have a centralized place for this and I really don't want to replace original class with the subclass created.Rosalia
L
1

If you wish to store a string in your UIImageView (or any UIView for that matter), try the following-

Set the accessibility identifier in your view, "l"

{
    l.accessibilityIdentifier = @"your string here";
}

Get the UIView, "l," from your gesture recognizer:

-(void)deleteImage:(UILongPressGestureRecognizer*)sender { 
    if(UIGestureRecognizerStateBegan == sender.state) {

    UIView *view = sender.view;
    NSString *storedString = view.accessibilityIdentifier;

    }
}

storedString is the string stored in your UIView. Hope this helps anyone in the future!

Leniency answered 15/2, 2015 at 21:14 Comment(4)
You shouldn't do that.Kincardine
@shane, it should be obvious. accessibilityIdentifier is meant to be used for automated UI testing, not for storing information.Kincardine
If that were so obvious maybe he wouldn't have suggested it. Next time include that in your original comment if you want to be a better stackoverflow commenter. Cheers.Gourmet
Poor form shane. Clearly when the property is called -accessibilityIdentifier you shouldn't be storing random data there and Apfelsaft was fine in not needing to explain further. Be more courteous next time.Indelicate
S
0

No, you cannot keep info into an imageView instance. Even if you find writable string property of imageView, this will be a wrong approach. Do this instead: assign an order number to imegeview.tag property, and keep an NSMutableDictionary for the info, thus, the imageView.tag you will use as the key, and the info will be the value.

Saturant answered 21/7, 2012 at 18:6 Comment(3)
You can actually, see my answerBismuth
sorry I am not familiar with thatSaturant
thanks for saying I am incompetent, this leads to lower my lack of experience, by forcing the studySaturant

© 2022 - 2024 — McMap. All rights reserved.