I've subclassed UIImageView, it's called ACCascadeImageView.
#interface ACCascadeImageView : UIImageView{
BOOL isSpotlight;
}
#property (assign, nonatomic) BOOL isSpotlight;
-----
#implementation ACCascadeImageView
#synthesize isSpotlight;
I then create instances like so, and add a gesturerecognizer..
ACCascadeImageView *imageview =
[[ACCascadeImageView alloc] initWithFrame:imageframe];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[imageview addGestureRecognizer:singleTap];
In the handleSingleTap method, I loop through my UIScollView subviews, and I try to do this for each one...
(imageview in this scope is [gestureRecognizer view])
[imageview setIsSpotlight:NO];
But I get this...
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[UIImageView setIsSpotlight:]: unrecognized selector sent to instance 0x6888be0'
Why has my ACCascadeImageView suddenly become UIImageView? I apologize if I'm doing something stupid, but I've subclassed UIImageView before just fine. I'm confused.
I should say that I've NSLog'd [imageview class] and I get, "ACCascadeImageView".
Here's the problem:
NSArray *cascadeImages = [PhotoCascade subviews];
for (ACCascadeImageView *v in cascadeImages){
NSLog(#"RESPONDS: %d", [v respondsToSelector:#selector(setIsSpotlight:)]);
[v setIsSpotlight:NO];
}
I get :
RESPONDS: 1
RESPONDS: 0
Then it dies.
You can't be sure [gestureRecognizer view] is your UIImageView. To check this try NSLog(#"view: %#", [gestureRecognizer.view class]);. My tests says it's just UIView.
If you're adding gesture recognizer to your image view the selector will be fired only when the user taps this view. So you could omit those checks at all.
By your results, and since you only test once per object, it seems the first object in the array is your custom subview, but the second is not. Maybe somehow one of the objects in cascadeImages isn't an ACCascadeImageView. Step through the array and perform introspection on each one, logging the results, to make sure the array only contains ACCascadeImageViews.
You're saying that your handleSingleTap is returning TRUE in response to respondsToSelector:#selector(setIsSpotlight:), but then when you try to use setIsSpotlight, it fails? That is a mystery.
For example, I've created this super simple example, and it works as you'd expect:
#import "ViewController.h"
#interface Test : UIImageView
#property (assign, nonatomic) BOOL isSpotlight;
#end
#implementation Test
#synthesize isSpotlight;
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
Test *test = [[Test alloc] initWithFrame:self.view.frame];
[self.view addSubview:test];
test.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[test addGestureRecognizer:tap];
}
- (void)handleSingleTap:(UIGestureRecognizer *)sender
{
Test *imageview = (Test *)[sender view];
NSLog(#"%s %d", __FUNCTION__, [imageview respondsToSelector:#selector(setIsSpotlight:)]);
[imageview setIsSpotlight:NO];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
The problem has to be something simple, but with limited code snippets provided in your question, it's not obvious what the problem is. But the code you've provided above would not manifest the sort of problem you describe. There must be some rogue UIImageView in your app!
Related
I have several UIImageViews contained by a UIView. My idea is when a UIImageView is touched, the UIView can know which one is touched thru delegate.
Here's my code
MyImageView.h
#protocol MyImageViewDelegate
-(void)toucheDelegate:(id)sender;
#end
#interface MyImageView:UIImageView{
id<MyImageViewDelegate> delegate;
}
#property(assign) id<MyImageViewDelegate> delegate;
#end
MyImageView.h
#implementation MyImageView
#synthesize delegate
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.delegate touchDelegate:self];
}
MyView.m
-(void)functionA{
for(int i=0;i<4;i++){
MyImageView *imageView=[[MyImageView alloc] init];
imageView.delegate=self; //Here it gives a warning saying Assigning to 'id<MyImageViewDelegate>' from incompatible type 'MyView *'
[self addSubview:imageView];
[imageView release];
}
}
-(void)touchDelegate:(id)sender{
NSLog(#"in superview");
}
The exception is unrecognized seletor sent to instance 0x7526c30. What does this mean?
I'm new to objective c and I'm not very clear about this delegate thing or the warning I mentioned in the code block. What's the problem? Any suggestion?
You Can Implement in this way
Create and Image then enable userInteraction by [imageView setUserInteractionEnabled:YES] Then add tag and Give UITapGestureRecognizer with a selector that's all you need to doo.
for(int i=0;i<4;i++){
MyImageView *imageView=[[MyImageView alloc] init];
[imageView setUserInteractionEnabled:YES];
[self.view addSubview:imageView];
[self.imageView setUserInteractionEnabled:YES];
[self.imageView setImage:[UIImage imageNamed:#"delete.png"]];
[self.imageView setTag:i];
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgTouchUp:)];
tapped.numberOfTapsRequired = 1;
[self.imageView addGestureRecognizer:tapped];
}
Then In selector recognize UIImaged taped by their respective Tags which can be found as shown bellow.
-(void) imgTouchUp:(id)sender {
UITapGestureRecognizer *gesture = (UITapGestureRecognizer *) sender;
NSLog(#"Taped Image tag is %d", gesture.view.tag);
}
You can Also find various Gestures.
UITapGestureRecognizer.
UIPinchGestureRecognizer.
UIRotationGestureRecognizer.
UISwipeGestureRecognizer.
UIPanGestureRecognizer.
UILongPressGestureRecognizer.
I have a view which includes two subviews. I had it working so that only one subview was shown at a time and each subview had a button and when the button was clicked the subview would flip over and the next subview would appear. The problem was that it appeared as though the entire view was flipping. After reading on this site about how to solve the problem I attempted to add the subviews to a container and flip that instead. However now, although my first subview is showing up when I press the button it no longer flip. It doesn't do anything. I put a log statement in the method which flips the subviews, as well as a breakpoint and as far as I can tell it no longer gets called. I'm very new to xcode and objective c and delegates and I have no idea how to proceed. Any help would be appreciated. Thanks.
I have included the relevant code here:
The header for the ViewController
#interface ExerciseViewController : UIViewController<ExerciseSubViewDelegate>
//stuff for subviews
#property (nonatomic, strong) ExerciseSubViewImage *subViewImage;
#property (nonatomic, strong) ExerciseSubViewText *subViewText;
#property UIView *panel;
#end
This is the code for the ViewController:
#interface ExerciseViewController ()
#end
#implementation ExerciseViewController
#synthesize subViewImage, subViewText;
- (void)viewDidLoad
{
self.subViewImage.delegate = self;
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
}
-(ExerciseSubViewImage *)subViewImage
{
if (!subViewImage)
{
CGRect subViewImageFrame = CGRectMake(0,0, _panel.bounds.size.width, _panel.bounds.size.height);
self.subViewImage = [[ExerciseSubViewImage alloc] initWithFrame:subViewImageFrame];
[_panel addSubview:subViewImage];
}
return subViewImage;
}
-(ExerciseSubViewText *)subViewText
{
if (!subViewText)
{
CGRect subViewTextFrame = CGRectMake(0,0, _panel.bounds.size.width, _panel.bounds.size.height);
self.subViewText = [[ExerciseSubViewText alloc] initWithFrame:subViewTextFrame];
self.subViewText.backgroundColor = [UIColor blueColor];
[_panel addSubview:subViewText];
}
return subViewText;
}
-(void)exerciseSubViewImagePressed
{
[UIView transitionWithView:_panel
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[subViewImage removeFromSuperview];
[_panel addSubview:subViewText];
}
completion: nil];
//This is how I did it before I added the container
/*[UIView transitionFromView:subViewImage
toView:subViewText
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
self.subViewText.delegate = self;*/
NSLog(#"Ipushedtheimage");
}
-(void)exerciseSubViewTextPressed
{//I haven't updated this yet
[UIView transitionFromView:subViewText
toView:subViewImage
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
self.subViewImage.delegate = self;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
subViewImage = nil;
subViewText = nil;
}
#end
This is the code for the delegate
#import
#protocol ExerciseSubViewDelegate <NSObject>
-(void) exerciseSubViewImagePressed;
-(void) exerciseSubViewTextPressed;
#end
I am also added the code for the first subview:
#import
#import "ExerciseSubViewDelegate.h"
#interface ExerciseSubViewImage : UIView
#property (nonatomic, strong) UIButton *button;
#property (nonatomic, assign) id<ExerciseSubViewDelegate>delegate;
#property (strong, nonatomic) UIImageView *exerciseImageView;
#end
#import "ExerciseSubViewImage.h"
#import "UIImage+animatedGIF.h"
#implementation ExerciseSubViewImage
#synthesize button;
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//Initialization code
self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
CGRect buttonFrame = CGRectMake(50,200,100,35);
self.button.frame = buttonFrame;
[self.button setTitle:#"Image"forState:UIControlStateNormal];
[self.button addTarget:self
action:#selector(buttonTouched)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.button];
_exerciseImageView = [[UIImageView alloc] initWithFrame:CGRectMake(50,20,160,158)];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"AppleLogo" withExtension:#"gif"];
_exerciseImageView.image = [UIImage animatedImageWithAnimatedGIFURL:url];
[self addSubview:self.exerciseImageView];
}
return self;
}
-(void)buttonTouched
{
NSLog(#"imagebuttonpressed");
[self.delegate exerciseSubViewImagePressed];
}
Again, any help would be appreciate. I know I'm probably just not understanding something simple.
Ok. This took me all weekend but I finally figured it out on my own. I thought I would shere the answer here in case anyone else ever has a similar problem. After trying several other approaches I finally went back to the approach I used here and started inserting a whole bunch of NSLogs to determine the order that every thing was executing in. What I finally ended up doing was changing this: (all in the top ViewController)
self.subViewImage.delegate = self;
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
to this:
//create panel
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, s self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
//Set the subview delegates
self.subViewImage.delegate = self;
self.subViewText.delegate = self;
I've got a problem - I'm getting EXC_BAD_ACCESS when trying to set UIWebView.delegate = self;
My code:
vkLogin.h -
#import UIKit/UIKit.h
#interface vkLogin : UIViewController <UIWebViewDelegate>
{
UIWebView *authBrowser;
UIActivityIndicatorView *activityIndicator;
}
#property (nonatomic, retain) UIWebView *authBrowser;
#property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
#end
vkLogin.m -
#import "vkLogin.h"
#import "bteamViewController.h"
#implementation vkLogin
#synthesize authBrowser;
- (void) viewDidLoad
{
[super viewDidLoad];
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
activityIndicator.autoresizesSubviews = YES;
activityIndicator.hidesWhenStopped = YES;
[self.view addSubview: activityIndicator];
[activityIndicator startAnimating];
authBrowser = [[UIWebView alloc] initWithFrame:self.view.bounds];
authBrowser.delegate = self;
authBrowser.scalesPageToFit = YES;
[self.view addSubview:authBrowser];
NSString *authLink = #"http://api.vk.com/oauth/authorize?client_id=-&scope=audio&redirect_uri=http://api.vk.com/blank.html&display=touch&response_type=token";
NSURL *url = [NSURL URLWithString:authLink];
[authBrowser loadRequest:[NSURLRequest requestWithURL:url]];
}
- (void) webViewDidFinishLoad:(UIWebView *)authBrowser
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Lol" message:#"OLOLO" delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil, nil];
[alert show];
}
#end
So, if i'm commeting delegate string - everything working fine, but I didn't recieve my webViewDidFinishLoad event.
What I'm doing wrong?
The error isn't in the code you have posted. Your zombie message is saying your reference to vkLogin is bad. So you need to look at whatever class creates and holds a reference to your vkLogin class.
That class should be doing something like a vkLogin *foo = [[vkLogin alloc] init];
Update:
Based on your comments it looks like you are creating a local variable for vkLogin. It would be most useful to see the code creates and uses vkLogin and how it's called. Barring that, here are a few guesses.
You are called the method which creates and adds vkLogin to a subView more than once. (Each time would create a new instance).
You have some sort of call back which can occur after vkLogin has been removed.
My guess is vkLogin should be a property in your class, not a local method variable.
in your .h you would add
#proprerty (strong, nonatomic) vkLogin *vk;
and in your .m file you could refer to it as self.vk so you'd create it and add it as a subview like:
self.vk = [[vkLogin alloc] init];
[self.view addSubview:self.vk];
On a side note, convention says we should start class names with a capital letter, so you'd name the class VkLogin which would make it easily distinguishable from a variable named vkLogin (but worry about that after you solve the problem)
This is my first iOS app, so I am probably missing something very simple. Please be kind. I have been tearing my hair out and I could really use some help.
Overview Of App
Basically, this is a single page application that just loads a UIWebView. I have an external accessory (bluetooth barcode scanner) that I connect and basically what I want to do is when the the app receives a scan, I want to call a method in my ViewController and update the UIWebView accordingly.
What Is Working
I am able to connect the scanner, load the first view, which loads the initial webpage, scan a barcode and call the method in my controller.
My Problem
I can't seem to figure out how to update the UIWebView from the method in my controller. It logs the url string to my debugger area, but never actually updates the webview. I am pretty sure I have some delegation wrong or something with my webview instance. There must be some glue code here that I am missing.
My Code HelloWorldViewController.h
#import <UIKit/UIKit.h>
#import "KScan.h"
#interface HelloWorldViewController : UIViewController <UIWebViewDelegate> {
IBOutlet UIWebView *page;
IBOutlet UILabel *myLabel;
Boolean IsFirstTime;
KScan *kscan;
}
- (void)setFirstTime;
- (void)DisplayConnectionStatus;
- (void)DisplayMessage:(char *)Message;
- (void)newBarcodeScanned:(NSString *)barcode;
- (void)loadBarcodePage:(NSString *)barcode;
#property (nonatomic, retain) KScan *kscan;
#property (nonatomic, retain) UIWebView *page;
#property (nonatomic, retain) UILabel *myLabel;
#end
My Code HelloWorldViewController.m
#import "HelloWorldViewController.h"
#import "common.h"
#implementation HelloWorldViewController
#synthesize myLabel;
#synthesize page;
#synthesize kscan;
- (void)setFirstTime
{
IsFirstTime = true;
}
- (void)viewDidLoad
{
self.kscan = [[KScan alloc] init];
[super viewDidLoad];
page.scrollView.bounces = NO;
//page.delegate = self;
[page loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.0.187:3000"]]];
}
- (void) newBarcodeScanned:(NSString *)barcode
{
NSLog(#"%s[%#]",__FUNCTION__, barcode);
[self loadBarcodePage:barcode];
}
- (void)loadBarcodePage:(NSString *)barcode
{
NSLog(#"%s",__FUNCTION__);
NSString *url = [[NSString alloc] initWithFormat:#"http://www.google.com/%#", barcode];
NSLog(#"%#", url);
[page loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
}
- (void)viewDidUnload
{
[myLabel release];
myLabel = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
- (void)dealloc {
[page release];
[kscan release];
[myLabel release];
[super dealloc];
}
#end
Basically, I am just trying to load google.com into my page webview when scanning a barcode. My log statements are being logged with the correct URL, but this line of code doesn't work.
[page loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
I am not getting any errors and my xCode debugging skills are not the greatest.
Any help would be greatly appreciated!
It looks like your webview is never allocated, or added to your main view. You are probably talking to a nil instance.
Unless your web view comes from a XIB file (which I doubt since it is not declared as an IBOutlet in your heder file) try adding something like this to your viewDidLoad:
self.page = [[UIWebView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.page];
I have an IntroViewController with a very large image, which increases the memory of my app by about 1.5MB. The image is set on a UIImageView within the View Controller's NIB.
Once the intro has finished, I call release on the IntroViewController, which then successfully calls dealloc on itself and calls release on the large UIImageView. However, my memory in Instruments doesn't seem to come back down again. I can test the memory difference by simply renaming the image to the UIImageView in the NIB so it can't find anything, and then the memory drops by about 1.5MB.
So why am I not getting that memory back? Is there something that I'm missing which is stopping the UIImageView from being properly dealloc'd?
Thanks,
:-Joe
------- ADDED: CODE ------
#import <UIKit/UIKit.h>
#class AppDelegate_iPhone;
#interface IntroViewController : UIViewController
{
AppDelegate_iPhone *appDelegate;
UIImageView *wheelImageView;
UIView *copyOverlayView;
}
#property (nonatomic, retain) IBOutlet UIImageView *wheelImageView;
#property (nonatomic, retain) IBOutlet UIView *copyOverlayView;
- (void)startAnimation;
#end
#implementation IntroViewController
#synthesize wheelImageView, copyOverlayView;
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
[self startAnimation];
}
- (void)viewDidDisappear:(BOOL)animated
{
[self.wheelImageView removeFromSuperview];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.wheelImageView.image = nil;
appDelegate = nil;
}
- (void)dealloc
{
[wheelImageView release];
[copyOverlayView release];
[super dealloc];
}
- (void)startAnimation
{
self.wheelImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView beginAnimations:#"wheelScale" context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(wheelScaleDidFinish)];
self.wheelImageView.transform = CGAffineTransformMakeScale(1.0, 1.0);
[UIView commitAnimations];
[UIView beginAnimations:#"copyOverlayFade" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay:1.0];
self.copyOverlayView.alpha = 0;
[UIView commitAnimations];
}
- (void)wheelScaleDidFinish
{
[appDelegate introAnimationFinished];
}
#end
---- EDIT
Can anyone please help? I'm stumped, and it's causing my app to crash on the iPhone due to high memory :( can't figure out how to get rid of the stupid image! Grrrr...
The UIImageView is being released, but are you sure you don't have a phantom retain on it somewhere else? If I were you I would try to track down its retains and releases. You can either do that with funky debugger tricks, tools like malloc_history, or simply by make a subclass of UIImageView:
#interface DebugImageView #end
#implementation DebugImageView
- (id) retain {
NSLog(#"retained");
return [super retain];
}
- (void) release {
NSLog(#"release");
[super release];
}
- (void) dealloc {
NSLog(#"dealloc");
[super dealloc];
}
Just change the class of the UIImageView object in IB to DebugImageView, and you should be good to go. You can also put break points on those to see where they are called, or just make calls to backtrace() where I have logging statements.
If the object is in fact being deleted, then your memory increase is probably not serious, and is most likely due to either a large reusable buffer object that is lazily evicted, or heap fragmentation. 1.5MB does seem a bit high for that sort of thing though, so I suspect it is a an over retain.
Okay so I found the solution, but am very surprised by it!
Not only did I have to take the UIImageView out of IB and create it dynamically, I had to avoid the use of the convenience method for loading the image. Instead of:
wheelImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"wheel-intro.png"];
[self.view addSubview:self.wheelImageView];
[wheelImageView release];
I had to use:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"wheel-intro" ofType:#"png"];
wheelImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
wheelImageView = [[UIImageView alloc] initWithImage:wheelImage];
[wheelImage release];
[self.view addSubview:self.wheelImageView];
[wheelImageView release];
Now when I do [wheelImageView removeFromSuperview] in the viewDidDisappear handler, everything gets cleaned up correctly.
What I find REALLY bizarre, is that putting the image in Interface Builder is identical in memory management to using a convenience method in XCode. And neither work properly! Note to self: Stop using convenience methods...