0

I have a parent view that allows you to see post in a UITableView. In its Navigation Bar I have a post button that when pressed presents a UIView subclass and shows it on the top of the screen. I have an image on that UIView that when tapped it presents the UIImagePickerController to allow users to pick an image to post to the service. I then convert the image to a string using base64encoding and everything is fine inside of that function if I use NSLog to do print outs. However, when I go to post and call back to the NSString value from the base64encoding I get a null value. It seems like ARC doesn't manage the same way in a UIView subclass as it does in a UIViewController, because this code works on a viewController, just not retaining on my subview. Code is below... Any direction you can provide will be much appreciated, I have played with the properties for these values but nothings seems to make ARC retain it. .h file

    @interface PostView: UIView
    {
        CGFloat animatedDistance;
        CGRect originalFrame;
        BOOL isShown;
        RESTURLDelagate *_connection;
        MBProgressHUD *HUD;
        NSMutableData *_data;
        NSData *imageData;
        UIAlertView *noConnection, *userSetup, *userExist, *accountAlertView, *confirmed, *login;
    }
    @property (weak, nonatomic) IBOutlet UIButton *postButton;
    @property (nonatomic, strong) IBOutlet UILabel *attachedLabel;
    @property (weak, nonatomic) IBOutlet UITextView *textView;
    @property (nonatomic, strong) IBOutlet UILabel *characterLimit;
    @property (nonatomic, strong) IBOutlet UIImageView *attachImage;
    @property (strong) NSString *encodedImage;
    - (NSString *)Base64Encode:(NSData *)data;
    - (IBAction)postAction:(id)sender;

    - (void)show;
    - (void)hide;

    @end

.m file

    @implementation PostView

    @synthesize attachedLabel;
    @synthesize postButton;
    @synthesize textView;
    @synthesize characterLimit;
    @synthesize attachImage;
    @synthesize encodedImage;

    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            originalFrame = frame;
            NSArray *xib = [[NSBundle mainBundle] loadNibNamed:@"PostView" owner:self options:nil];
            PostView *view = [xib objectAtIndex:0];
            [view setBackgroundColor:[UIColor whiteColor]];
            [view setAlpha:0.7f];
            attachedLabel = [[UILabel alloc] initWithFrame:CGRectMake(204, 212, 56, 21)];
            attachedLabel.textColor = [UIColor blackColor];
            [attachedLabel setText:@"Attached"];
            [attachedLabel setHidden:YES];
            attachedLabel.backgroundColor = [UIColor clearColor];
            attachedLabel.font = [UIFont fontWithName:text_font_name size:12.0];
            characterLimit = [[UILabel alloc] initWithFrame:CGRectMake(246, 13, 50, 21)];
            [characterLimit setTextAlignment:NSTextAlignmentRight];
            characterLimit.textColor = [UIColor blackColor];
            characterLimit.backgroundColor = [UIColor clearColor];
            characterLimit.font = [UIFont fontWithName:text_font_name size:12.0];
            attachImage = [[UIImageView alloc] initWithFrame:CGRectMake(270, 208, 30, 30)];
            [attachImage setImage:[UIImage imageNamed:@"attachphoto30x30.png"]];
            [self.textView setDelegate:self];
            [self.textView setAlpha:0.7f];
            [self.textView setTextColor:[UIColor whiteColor]];
            [self.textView setBackgroundColor:[UIColor clearColor]];
            self.layer.cornerRadius = 10.0f;
            self.layer.masksToBounds = YES;
            [self addSubview:view];
            [self addSubview:characterLimit];
            [self addSubview:attachedLabel];
            [self addSubview:attachImage];
        }
        return self;
    }

    - (IBAction)openCamera:(id)sender
    {
        UIImagePickerController *controller = [[UIImagePickerController alloc] init];
        controller.delegate = self;
        [(ShamePostViewController *)[self.superview nextResponder] presentViewController:controller animated:YES                 
            completion:nil];
    }

    -(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
    {
        [picker dismissViewControllerAnimated:YES completion:nil];
        UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage];
        //UIImage *scale = [image scaleToSize:CGSizeMake(320.0f, 548.0f)];
        imageData = [[NSData alloc] initWithData:UIImageJPEGRepresentation(image, 1)];
        encodedImage = [self Base64Encode:imageData];
        [attachedLabel setHidden:NO];
    }

    - (void)show
    {
        //prepare attachImage
        attachImage.userInteractionEnabled = YES;
        UITapGestureRecognizer *tapAttach = [[UITapGestureRecognizer alloc]
                                             initWithTarget:self action:@selector(openCamera:)];
        tapAttach.numberOfTapsRequired = 1;
        [self.attachImage addGestureRecognizer:tapAttach];


        isShown = YES;
        self.transform = CGAffineTransformMakeScale(0.1, 0.1);
        self.alpha = 0;
        [UIView beginAnimations:@"showAlert" context:nil];
        [self setBackgroundColor:[UIColor clearColor]];
        [UIView setAnimationDelegate:self];
        self.transform = CGAffineTransformMakeScale(1.1, 1.1);
        self.alpha = 1;
        [UIView commitAnimations];
    }

    - (void)hide
    {
        isShown = NO;
        [UIView beginAnimations:@"hideAlert" context:nil];
        [UIView setAnimationDelegate:self];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"hidePostView_Notification" object:nil];
        self.transform = CGAffineTransformMakeScale(0.1, 0.1);
        self.alpha = 0;
        [UIView commitAnimations];
    }

    - (void)toggle
    {
        if (isShown)
        {
            [self hide];
        } else
        {
            [self show];
        }
    }

    #pragma mark Animation delegate

    - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
    {
        if ([animationID isEqualToString:@"showAlert"])
        {
            if (finished)
            {
                [UIView beginAnimations:nil context:nil];
                self.transform = CGAffineTransformMakeScale(1.0, 1.0);
                [UIView commitAnimations];
            }
        } else if ([animationID isEqualToString:@"hideAlert"])
        {
            if (finished)
            {
                self.transform = CGAffineTransformMakeScale(1.0, 1.0);
                self.frame = originalFrame;
            }
        }
    }
    #pragma mark BaseEncode64

    - (NSString *)Base64Encode:(NSData *)data
    {
        //Point to start of the data and set buffer sizes
        int inLength = [data length];
        int outLength = ((((inLength * 4)/3)/4)*4) + (((inLength * 4)/3)%4 ? 4 : 0);
        const char *inputBuffer = [data bytes];
        char *outputBuffer = malloc(outLength);
        outputBuffer[outLength] = 0;

        //64 digit code
        static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

        //start the count
        int cycle = 0;
        int inpos = 0;
        int outpos = 0;
        char temp;

        //Pad the last to bytes, the outbuffer must always be a multiple of 4
        outputBuffer[outLength-1] = '=';
        outputBuffer[outLength-2] = '=';

        while (inpos < inLength){
            switch (cycle) {
                case 0:
                    outputBuffer[outpos++] = Encode[(inputBuffer[inpos]&0xFC)>>2];
                    cycle = 1;
                    break;
                case 1:
                    temp = (inputBuffer[inpos++]&0x03)<<4;
                    outputBuffer[outpos] = Encode[temp];
                    cycle = 2;
                    break;
                case 2:
                    outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xF0)>> 4];
                    temp = (inputBuffer[inpos++]&0x0F)<<2;
                    outputBuffer[outpos] = Encode[temp];
                    cycle = 3;
                    break;
                case 3:
                    outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xC0)>>6];
                    cycle = 4;
                    break;
                case 4:
                    outputBuffer[outpos++] = Encode[inputBuffer[inpos++]&0x3f];
                    cycle = 0;
                    break;
                    default:
                    cycle = 0;
                    break;
            }
        }
        NSString *pictemp = [NSString stringWithUTF8String:outputBuffer];
        free(outputBuffer);
        return pictemp;
    }

    #pragma Submit action for posting
    - (IBAction)postAction:(id)sender
    {
        if ([textView.text isEqualToString:@""])
        {
            UIAlertView *noText = [[UIAlertView alloc] initWithTitle:@"Missing Content" message:@"You must enter a message to post before submitting." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil, nil];
            [noText show];
        }else
            if ([[[NSUserDefaults standardUserDefaults] valueForKey:@"auth_id"] isEqualToString:@"1"])
            {
                _data = [[NSMutableData alloc] init];
                URLSingleton *urls = [URLSingleton sharedInstance];
                REQUESTBuilderDelagate *rbd = [[REQUESTBuilderDelagate alloc] init];
                NSString *blob_ind = [[NSString alloc] init];
                NSLog(@"%@", encodedImage);
                "STRING IS NULL HERE"
                if ([encodedImage length] > 0)
                {
                    blob_ind = @"1";
                }else
                    blob_ind = @"0";

                NSArray *keys = [[NSArray alloc] initWithObjects:@"token", @"postMsg", @"active", @"blob", nil];
                NSArray *values = [[NSArray alloc] initWithObjects:[[NSUserDefaults standardUserDefaults]         
                    valueForKey:@"session_token"], textView.text, @"1", blob_ind, nil];
                [rbd createJSONRequest:keys values:values];
                [rbd setPostURL:urls.postMessage];

                _connection = [[RESTURLDelagate alloc] initWithRequest:rbd.getPostURL delegate:self];
                [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
                [_connection setDescription:@"PVCPOSTMSG"];
                HUD = [MBProgressHUD showHUDAddedTo:self animated:YES];
                HUD.labelText = @"Posting...";
                [HUD setConnection:_connection];
                [_connection start];
                [postButton setEnabled:NO];
            }
            else
            {
                login = [[UIAlertView alloc] initWithTitle:@"Authentication Required" message:@"Please login before attempting to post!" delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil, nil];
                [login show];
                [textView setText:@""];
                [characterLimit setText:@"0/160"];
            }

    }

    #pragma Post Image
    -(void)postImage:(NSString *)shameID
    {
        if ([[[NSUserDefaults standardUserDefaults] valueForKey:@"auth_id"] isEqualToString:@"1"])
        {
            URLSingleton *urls = [URLSingleton sharedInstance];
            REQUESTBuilderDelagate *rbd = [[REQUESTBuilderDelagate alloc] init];

            NSArray *keys = [[NSArray alloc] initWithObjects:@"token", @"encode64", @"active", @"SID", nil];
            NSArray *values = [[NSArray alloc] initWithObjects:[[NSUserDefaults standardUserDefaults] 
                    valueForKey:@"session_token"], encodedImage, @"1", SID, nil];

            [rbd createJSONRequest:keys values:values];
            [rbd setPostURL:urls.imagePost];

            _connection = [[RESTURLDelagate alloc] initWithRequest:rbd.getPostURL delegate:self];
            [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
            [_connection setDescription:@"PVCADDIMAGE"];
            HUD = [MBProgressHUD showHUDAddedTo:self animated:YES];
            HUD.labelText = @"Attaching Image...";
            [postButton setEnabled:NO];
            [HUD setConnection:_connection];
            [_connection start];
        }
    }

    @end
Jarod
  • 193
  • 1
  • 10
  • encodedImage is what is not being retained. – Jarod Apr 30 '13 at 18:51
  • what property you set for encodedImage? – Augustine P A Apr 30 '13 at 19:00
  • property (nonatomic, assign) NSString *encodedImage; I have tried retain and copy also with no luck. – Jarod Apr 30 '13 at 19:01
  • 1
    Change the assign to retain or strong – Augustine P A Apr 30 '13 at 19:03
  • Just edited my post to add the properties from the h file. – Jarod Apr 30 '13 at 19:03
  • Just changed it to strong and I have also done retain with no luck. Here is a NSLog of the encodeImage when I call it on the post button action. 2013-04-30 14:04:41.240 [38028:907] (null) – Jarod Apr 30 '13 at 19:05
  • When posting, again you are calling base64 encoding method? Can you give the posting method also? – Augustine P A Apr 30 '13 at 19:08
  • Let me edit and just put the whole class m and h files in it – Jarod Apr 30 '13 at 19:08
  • There is no variable named `encodedImage` in this code that I can see. The synthesized instance variable should be `_encodedImage`. How is this compiling? Do you declare an `encodedImage` variable somewhere? – Chuck Apr 30 '13 at 19:27
  • Did you look at what I posted? Look at the .h file – Jarod Apr 30 '13 at 19:31
  • 1
    I don't know if this has anything to do with your problem, but if you're going to go to the trouble of creating properties, then you should use them. You're accessing the ivars directly. You should delete any ivar declarations for which you also create properties, delete the @synthesize statements (this is all done automatically now by the compiler), and refer to your properties with self.propertyName. – rdelmar Apr 30 '13 at 19:38
  • I am always interested in understanding better different ways to code in objective-c. I have changed to what you are saying and it is still not working.. – Jarod Apr 30 '13 at 19:58
  • I don't think this has anything to do with ARC -- If you have encodedImage as a strong property of PostView, then that should not be deallocated as long as PostView is alive. I tried doing a modified version of your app, and logging the encodedImage (right after creation) took a long time, and seem to hang my app. Are you seeing this? If I commented out the base 64 code, and just returned a small string, it logged correctly in the postAction method. – rdelmar Apr 30 '13 at 20:56
  • If the image isn't scaled down and you try to do a nslog on the string it will hang xcode. – Jarod Apr 30 '13 at 21:01
  • Can you post your sample code so I can see what you are doing in comparison to what I am doing? – Jarod Apr 30 '13 at 21:08
  • @rdelmar can you please post your code so I can compare what you are doing? Remember this is a UIView not a ViewController because I was able to get it working with the UIView Controller but that does not fit into my current model. – Jarod Apr 30 '13 at 21:21
  • @Jarod: I did look at your .h file. It does not include an `encodedImage` instance variable, only a property with that name, which should create an ivar named `_encodedImage`. So where is this instance variable coming from? – Chuck Apr 30 '13 at 23:22
  • Still null even after adding NSString *encodedImage to the h file – Jarod Apr 30 '13 at 23:50
  • @Jarod: I'm not suggesting that you add an instance variable with that name. I'm saying that it sounds like you somehow have a variable you aren't accounting for floating around somewhere, because the line `encodedImage = [self Base64Encode:imageData]` should be an error but apparently isn't giving you one. Try taking out that instance variable, going to one of the places where you refer to `encodedImage` and jumping to the definition. – Chuck May 01 '13 at 00:01

3 Answers3

0

You just replace "encodedImage = [self Base64Encode:imageData];" with the line of code as given,

[self setEncodedImage:[self Base64Encode:imageData]];

Hope it will work :)

Augustine P A
  • 5,008
  • 3
  • 35
  • 39
  • When you are clicking the post button? After select an image or before selecting an Image? – Augustine P A Apr 30 '13 at 19:39
  • After selecting the image. – Jarod Apr 30 '13 at 19:52
  • After the image is selected in that function the NSString is assigned the result and I can do a NSLog to see that it is set to it. However, ARC must not be retaining it after it is out of that function and it is being dealloc'd and not retained. So when I call that encodedImage in the post button the object is null. – Jarod Apr 30 '13 at 19:59
0

You should change the property to @property (nonatomic, strong) NSString *encodedImage; and you should change the line encodedImage = [self Base64Encode:imageData]; to self.encodedImage = [self Base64Encode:imageData];

EDIT

So I saw this answer to a similar problem (https://stackoverflow.com/a/13640603/2315974) so my last idea is that you should move

 UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage];
     //UIImage *scale = [image scaleToSize:CGSizeMake(320.0f, 548.0f)];
     imageData = [[NSData alloc] initWithData:UIImageJPEGRepresentation(image, 1)];
     encodedImage = [self Base64Encode:imageData];
    [attachedLabel setHidden:NO];

inside the completion block of the dismissViewControllerAnimated method.

Community
  • 1
  • 1
danypata
  • 9,895
  • 1
  • 31
  • 44
  • It is still null, I am wondering if it is because I am dismissing the Image Picker View Controller and ARC assumes that those objects can be dealloc. – Jarod Apr 30 '13 at 20:09
  • Sorry but I have to ask, are you checking the nil value with `NSLog(@"%@",encodedImage)` ? – danypata Apr 30 '13 at 20:18
  • Yes that is what I am doing to see what is contained in the string and i get (null) – Jarod Apr 30 '13 at 20:20
  • You should try with `NSLog(@"%@",self.encodedImage)` or check it with `po self.encodedImage` in XCode cmd line – danypata Apr 30 '13 at 20:21
  • Even moving it to the completion block it is still null. – Jarod Apr 30 '13 at 21:06
0

This is not an answer, but a trial app based on yours, and it works ok. I added a smaller image to my camera roll on my phone, and now I get the string logging correctly in the postAction method (I copied the log statement to the top of the method since I didn't do anything with user defaults):

The only thing I changed in PostView was the postAction method. Here it is:

- (IBAction)postAction:(id)sender
{

     NSLog(@"EncodedImage is :%@", encodedImage);


    if ([textView.text isEqualToString:@""])
    {
        UIAlertView *noText = [[UIAlertView alloc] initWithTitle:@"Missing Content" message:@"You must enter a message to post before submitting." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil, nil];
        [noText show];
    }else
        if ([[[NSUserDefaults standardUserDefaults] valueForKey:@"auth_id"] isEqualToString:@"1"])
        {
            _data = [[NSMutableData alloc] init];
            //URLSingleton *urls = [URLSingleton sharedInstance];
            //REQUESTBuilderDelagate *rbd = [[REQUESTBuilderDelagate alloc] init];
            NSString *blob_ind = [[NSString alloc] init];
            NSLog(@"EncodedImage is :%@", encodedImage);
            //"STRING IS NULL HERE"
            if ([encodedImage length] > 0)
            {
                blob_ind = @"1";
            }else
                blob_ind = @"0";

            NSArray *keys = [[NSArray alloc] initWithObjects:@"token", @"postMsg", @"active", @"blob", nil];
            NSArray *values = [[NSArray alloc] initWithObjects:[[NSUserDefaults standardUserDefaults]
                                                                valueForKey:@"session_token"], textView.text, @"1", blob_ind, nil];
            //[rbd createJSONRequest:keys values:values];
            //[rbd setPostURL:urls.postMessage];

            //_connection = [[RESTURLDelagate alloc] initWithRequest:rbd.getPostURL delegate:self];
            [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
            //[_connection setDescription:@"PVCPOSTMSG"];
            //HUD = [MBProgressHUD showHUDAddedTo:self animated:YES];
            //HUD.labelText = @"Posting...";
            //[HUD setConnection:_connection];
            //[_connection start];
            [postButton setEnabled:NO];
        }
        else
        {
            login = [[UIAlertView alloc] initWithTitle:@"Authentication Required" message:@"Please login before attempting to post!" delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil, nil];
            [login show];
            [textView setText:@""];
            [characterLimit setText:@"0/160"];
        }

}

I put PostView on screen with this method in ViewController:

-(IBAction)addPostView:(id)sender {
    PostView *postView = [[PostView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:postView];
}

The .h file for PostView is the same as what you posted, except that I commented out a couple of ivars:

@interface PostView : UIView <UITextViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
    CGFloat animatedDistance;
    CGRect originalFrame;
    BOOL isShown;
    //RESTURLDelagate *_connection;
    //MBProgressHUD *HUD;
    NSMutableData *_data;
    NSData *imageData;
    UIAlertView *noConnection, *userSetup, *userExist, *accountAlertView, *confirmed, *login;
}
@property (weak, nonatomic) IBOutlet UIButton *postButton;
@property (nonatomic, strong) IBOutlet UILabel *attachedLabel;
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (nonatomic, strong) IBOutlet UILabel *characterLimit;
@property (nonatomic, strong) IBOutlet UIImageView *attachImage;
@property (strong) NSString *encodedImage;
- (NSString *)Base64Encode:(NSData *)data;
- (IBAction)postAction:(id)sender;

- (void)show;
- (void)hide;
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Can you show your .h file for PostView and also how you are setting encodedImage in your .m file? – Jarod Apr 30 '13 at 21:31
  • The only difference between our code is this line pv = [[PostView alloc] initWithFrame:CGRectMake(5, 50, 310, 245)]; – Jarod Apr 30 '13 at 21:38
  • What iOS version are you testing this against? I am during it on 6.1.3 on my iPhone to test the attaching of images. – Jarod Apr 30 '13 at 22:23
  • @Jarod, I'm also using 6.1.3. – rdelmar Apr 30 '13 at 23:42
  • Should I start over from scratch. Basically this app is in the app store and I am redoing the UI for v2.0 and it is involving a lot of deleting and adding of different views and I wonder if the project is just messed up in xcode since this works for you. – Jarod Apr 30 '13 at 23:51
  • @Jarod, I can't really say. Without a overall knowledge of the app, I can't think what might be going wrong for you. This is a decision that you'll have to make. – rdelmar Apr 30 '13 at 23:53
  • Is there anyway we can sync up on a chat or something and I can work through this with you. I just redid the app as a new project and it still didn't work. So I am not sure what is going on. – Jarod May 01 '13 at 01:06
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29216/discussion-between-rdelmar-and-jarod) – rdelmar May 01 '13 at 01:08