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)?

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Moe
  • 4,744
  • 7
  • 28
  • 37
  • Maybe you can use imageView.tag to store some info with number. – lu yuan Jul 21 '12 at 18:02
  • 1
    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. – Rob Jul 21 '12 at 18:14
  • 1
    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). – Rob Napier Jul 21 '12 at 18:39
  • @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. – Rob Jul 22 '12 at 01:23

4 Answers4

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>
nielsbot
  • 15,922
  • 4
  • 48
  • 73
  • 1
    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.. – nielsbot Jul 21 '12 at 18:13
  • +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. – Moe Jul 22 '12 at 01:03
  • 1
    Up to you--although I always favor composition (categories) vs subclassing. It's easier to avoid painting yourself into a corner. – nielsbot Jul 22 '12 at 01:17
  • nielsbot, this works if I #import ``. I’ll no longer need to use tags. Great! +1 – Greg May 23 '17 at 05:41
  • That's at the top.. although not expressed as an `#import` statement.. I could change it I guess... – nielsbot May 23 '17 at 06:30
  • I added that to be more explicit – nielsbot May 23 '17 at 06:31
  • nielsbot, I have posted a related question which I think you'd be able to answer if you can spare the time. https://stackoverflow.com/q/44178621/2348597 – Greg May 25 '17 at 10:50
  • @Greg Seems answered :) – nielsbot May 26 '17 at 19:29
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.

diederikh
  • 25,221
  • 5
  • 36
  • 49
  • 1
    Tag takes nint, storing the item ID in it worked for me as well – Pierre Feb 03 '17 at 11:35
  • 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. – Abdul Yasin Apr 09 '19 at 06:30
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!

Max Friedman
  • 445
  • 4
  • 15
  • 3
    You shouldn't do that. – Apfelsaft May 23 '16 at 19:20
  • 1
    @shane, it should be obvious. accessibilityIdentifier is meant to be used for automated UI testing, not for storing information. – Apfelsaft Jun 21 '16 at 18:07
  • 1
    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. – shane Jun 22 '16 at 23:34
  • 1
    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. – ZaBlanc Jul 27 '18 at 03:24
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.

John Smith
  • 2,012
  • 1
  • 21
  • 33