Objective-c : addTarget to UIButton not calling - objective-c

I tried to use addTarget to add an event to a UIButton like this
EGOImageButton* image = [[EGOImageButton alloc] initWithPlaceholderImage:[UIImage imageNamed:#"screen.png"]];
[image setImageURL:[NSURL URLWithString:thumbnail]];
[image setFrame:CGRectMake(x, y, self.cellsize, self.cellsize)];
[image setTag: [self.photoIDs count]-1];
[image addTarget:self action:#selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:image];
[image release];
The function definition is this:
-(void)buttonTouched:(id)sender {
NSLog(#"Thumbnail Clicked");
}
This is the code for EGOImageButton init function
- (id)initWithPlaceholderImage:(UIImage*)anImage {
return [self initWithPlaceholderImage:anImage delegate:nil]; }
- (id)initWithPlaceholderImage:(UIImage*)anImage delegate:(id<EGOImageButtonDelegate>)aDelegate {
if((self = [super initWithFrame:CGRectZero])) {
self.placeholderImage = anImage;
self.delegate = aDelegate;
[self setImage:self.placeholderImage forState:UIControlStateNormal];
}
return self;
}
Error is looking like this:
-[NSCFType buttonTouched:]: unrecognized selector sent to instance 0x6c804a0
2012-06-06 01:36:59.061 Photo Collage for Facebook[2416:16103] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType buttonTouched:]: unrecognized selector sent to instance 0x6c804a0'
* Call stack at first throw:
(
0 CoreFoundation 0x0046a5a9 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x01402313 objc_exception_throw + 44
2 CoreFoundation 0x0046c0bb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
3 CoreFoundation 0x003db966 __forwarding + 966
4 CoreFoundation 0x003db522 _CF_forwarding_prep_0 + 50

It's a bit strange it doesn't get called, since it seens to be set up correctly (https://stackoverflow.com/questions/8968446/cant-able-to-add-click-event-in-uibutton as a reference ). My first guess, without actually have access to your code, is that something is above your image and hence the click event is not passed to the UIButton sub-class (EGOImageButton).

Check to make sure the method your selector is refered to contains a sender of type UIButton
so like this -(void)buttonTouched:(UIButton *)sender

action:
#selector(buttonTouched:)
I think you don't need colon : here, remove it and try action: like this:
#selector(buttonTouched)

I think your buttonTouched method needs to have a return type of IBAction instead of void.

Related

App is fails when I try set customInfoViewController to nil

CustomInfoViewController property of AVPlayerViewController is nil by default.
https://developer.apple.com/documentation/avkit/avplayerviewcontroller/2868498-custominfoviewcontroller?language=objc
When I set new ViewController as custom all is fine:
UIViewController* vc = [[UIViewController alloc] init];
vc.preferredContentSize = CGSizeMake(1000, 100);
vc.title = #"TEST";
self.playerViewController.customInfoViewController = vc; // playerViewController is AVPlayerViewController
But when I try to set customInfoViewController back to nil (to hide it) - it always fails:
self.playerViewController.customInfoViewController = nil;
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
*** First throw call stack:
(
0 CoreFoundation 0x000000010746ea56 __exceptionPreprocess + 294
1 libobjc.A.dylib 0x0000000106537031 objc_exception_throw + 48
2 CoreFoundation 0x00000001074ae54c _CFThrowFormattedException + 194
3 CoreFoundation 0x000000010737bccd -[__NSPlaceholderArray initWithObjects:count:] + 237
4 CoreFoundation 0x0000000107382694 +[NSArray arrayWithObjects:count:] + 52
5 AVKit 0x000000010723997c -[AVPlayerViewController(AVPlayerViewControllerControls) setCustomInfoViewController:] + 88
Any suggestions how I can hide customInfoViewController?
Settings a property to nil will not hide it!
Depends on how you presented CustomInfoViewController.
If you presented it using present function, you should call dismiss and set it to nil inside the completion block.
if you presented it by simply adding its view to AVPlayerViewController's view, you need to remove it from its superview (using removeFromSuperview()) and only then, set it to nil.

Why this code no longer work in 7.1?

UITextField * textField= self.textField;
textField.background = [UIImage resizeableImageWithCapInsets2:UIEdgeInsetsMake(0, 7, 0, 7) withName:#"Search-Field"];
Simple code.
2014-03-13 09:31:02.099 isikota[179:60b] -[BGSearchBar setBackground:]: unrecognized selector sent to instance 0x17566b20
2014-03-13 09:32:17.720 isikota[179:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BGSearchBar setBackground:]: unrecognized selector sent to instance 0x17566b20'
It doesn't make sense. Clearly background is a property of textField
I found out that the problem is I use this:
-(UITextField *) textField
{
UIView * textFieldView = [self findASubViewWithProtocol:#protocol(UITextInputTraits)];
return (UITextField *)textFieldView;
}
This seems to be no longer the way to get a textField from UISearchBar. How to do so then?
I think the problem is since 7.1 [UISearchBar conforms to UITextInputTraits]
Also I wonder why
return (UITextField *)textFieldView;
doesn't return a run time error because now textFieldView is no longer a subclass of UITextField
I've always done it like this:
for (UIView* v in self.searchbar.subviews) {
if ([v isKindOfClass: [UITextField class]]) {
return v;
}
}
Or, in iOS 7:
for (UIView* v in [self.searchbar.subviews[0] subviews]) {
if ([v isKindOfClass: [UITextField class]]) {
return v;
}
}
Does that not work any more?

Horrible crash after trying to use MBProgressHud

I'm using MBPogressHUD in my project and I have all the libraries, imports, etc. set up correctly. However, I'm getting a crash that seems like it has to do with the selector methods. I have a method called "getInfo" that basically connects to a server. The HUD is triggered by pressing a button. After doing some research on the crash, people said to put the initialization in viewWillAppear because some of the init time takes up the time it takes to actually do the task and show the HUD.
-(void)viewWillAppear:(BOOL)animated {
connectionHUD = [[MBProgressHUD alloc]initWithView:self.view];
[self.view addSubview:connectionHUD];
connectionHUD.labelText = #"Connecting";
connectionHUD.mode = MBProgressHUDModeAnnularDeterminate;
}
-(IBAction)connectButton:(id)sender {
[connectionHUD showWhileExecuting:#selector(getInfo) onTarget:self withObject:nil animated:YES];
}
Crash:
Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
-(void)getInfo {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
UserPi *newPi = [[UserPi alloc]init];
newPi.passWord = self.passTextField.text;
[defaults setObject:self.passTextField.text forKey:#"password"];
newPi.userName = self.userTextField.text;
[defaults setObject:self.userTextField.text forKey:#"username"];
newPi.ipAddress = self.ipTextField.text;
[defaults setObject:self.ipTextField.text forKey:#"ip"];
[newPi connectToServer];
NSString* newAddress = [newPi returnIP];
self.connected = newPi.connected;
[self.delegate sendIP:newAddress];
[self.delegate isConnected:self.connected];
[defaults synchronize];
[self.navigationController popToRootViewControllerAnimated:YES];
}
full error:
bool _WebTryThreadLock(bool), 0x7327740: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
1 0x3a1ffe9 WebThreadLock
2 0x4ec8ff -[UITextRangeImpl isEmpty]
3 0x4ec4db -[UITextRange(UITextInputAdditions) _isCaret]
4 0x48e7b6 -[UITextSelectionView setCaretBlinks:]
5 0x328f79 -[UIKeyboardImpl setCaretBlinks:]
6 0x3185bc -[UIKeyboardImpl setDelegate:force:]
7 0x3184ae -[UIKeyboardImpl setDelegate:]
8 0x53ff65 -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
9 0x29215b -[UINavigationController navigationTransitionView:didStartTransition:]
10 0x418961 -[UINavigationTransitionView transition:fromView:toView:]
11 0x418658 -[UINavigationTransitionView transition:toView:]
12 0x294651 -[UINavigationController _startTransition:fromViewController:toViewController:]
13 0x29489b -[UINavigationController _startDeferredTransitionIfNeeded:]
14 0x295dc6 _popViewControllerNormal
15 0x296065 -[UINavigationController _popViewControllerWithTransition:allowPoppingLast:]
16 0xe6124b0 -[UINavigationControllerAccessibility(SafeCategory) _popViewControllerWithTransition:allowPoppingLast:]
17 0x2961a8 -[UINavigationController popViewControllerWithTransition:]
18 0x2965b9 -[UINavigationController popToViewController:transition:]
19 0x296257 -[UINavigationController popToRootViewControllerWithTransition:]
20 0x2961de -[UINavigationController popToRootViewControllerAnimated:]
21 0x4868 -[ConnectionViewController getInfo]
22 0x126a6b0 -[NSObject performSelector:withObject:]
23 0x8657 -[MBProgressHUD launchExecution]
24 0xca1805 -[NSThread main]
25 0xca1764 __NSThread__main__
26 0x95108ed9 _pthread_start
27 0x9510c6de thread_start
Most UIKit framework methods are not thread safe and must always be called on the main thread. In your case, getInfo is calling UIKit APIs from a background thread notably, -[UINavigationController popToRootViewControllerAnimated:]
MBProgressHUD causes your getInfo to be called on a background thread. See the method showWhileExecuting:onTarget:withObject:animated:.
Try using GCD to dispatch the UIKit methods on the main thread:
-(void)getInfo {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
UserPi *newPi = [[UserPi alloc]init];
newPi.passWord = self.passTextField.text;
[defaults setObject:self.passTextField.text forKey:#"password"];
newPi.userName = self.userTextField.text;
[defaults setObject:self.userTextField.text forKey:#"username"];
newPi.ipAddress = self.ipTextField.text;
[defaults setObject:self.ipTextField.text forKey:#"ip"];
[newPi connectToServer];
NSString* newAddress = [newPi returnIP];
self.connected = newPi.connected;
// **NOTE**
// PS: self.delegate methods should not call UIKit methods
// if they do, then move them into the main thread callback block
[self.delegate sendIP:newAddress];
[self.delegate isConnected:self.connected];
[defaults synchronize];
// Do UI work on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popToRootViewControllerAnimated:YES];
});
}
Are you calling -getInfo on the main thread? [self.navigationController popToRootViewControllerAnimated:YES]; must be called on the main thread.
The problem is that your getInfo method makes calls into the UI, like this:
[self.navigationController popToRootViewControllerAnimated:YES];
… but you're running it on a background thread.
The whole point of -showWhileExecuting: onTarget: withObject: animated: is that it automatically runs your code in a background thread.
So, you need to protect things the same way as you do when manually running in a background thread.
So, any UI code in your method needs to use performSelectorOnMainThread: and friends, or dispatch_async or other means of doing the same thing.
In this particular case, you want to dispatch a method that takes a primitive (BOOL) argument, which means you can't just use -performSelectorOnMainThread: withObject:. But you also presumably want to wait until it's done, which means you can't just use dispatch_async.
You can just write a wrapper method that takes no arguments:
- (void)popNavigationControllerToRoot {
[self.navigationController popToRootViewControllerAnimated:YES];
}
… and then:
[self performSelectorOnMainThread:#selector(popNavigationControllerToRoot)
waitUntilDone:YES];
Or you can use wrappers around NSInvocation, like the ones here:
[[self.navigationController dd_invokeOnMainThreadAndWaitUntilDone:YES]
popToRootViewControllerAnimated:YES];

NSTimer crashing my application

So I have an application which uses an NSTimer. The problem is that when the NSTimer runs, my application will crash with EXC_BAD_ACCESS. I only just started with objective-c so I don't know how to properly debug it. If I thought call the -(void)logIn{} with [self logIn]; the application will work.
My code:
.h
#interface DJ_WAppDelegate : NSObject {
NSTimer *loginTimer;
}
-(void)logIn;
#end
.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self logIn]; // this works
loginTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(logIn) userInfo:nil repeats:YES]; // this fails
}
- (void)logIn {
NSLog(#"Logging in...");
// if I comment out these 2 lines it works with the NSTimer!?... (I've would have more code below)
NSURL *loginConn = [NSURL URLWithString:[NSString stringWithFormat:#"some-website.com"]];
NSInteger loginReturn = [[NSString stringWithContentsOfURL:loginConn encoding:NSASCIIStringEncoding error:nil] intValue];
// when "loginReturn" return "OK" the timer (loginTimer) will be stopped with: [loginTimer invalidate];
// more code would be below... (which works!)
}
So the problem is with the NSURL it think.
Thanks for helping.
EDIT 1:
Here's the crash stack:
EException Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000020
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Application Specific Information:
objc_msgSend() selector name: respondsToSelector:
Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x9603bed7 objc_msgSend + 23
1 com.apple.CoreFoundation 0x922ed5f2 _CFStringAppendFormatAndArgumentsAux + 3138
2 com.apple.CoreFoundation 0x922ec979 _CFStringCreateWithFormatAndArgumentsAux + 105
3 com.apple.Foundation 0x9656ebfb -[NSPlaceholderString initWithFormat:locale:arguments:] + 163
4 com.apple.Foundation 0x9656eaae +[NSString stringWithFormat:] + 88
5 com.who.DJ-W 0x00001d56 -[DJ_WAppDelegate logIn] + 116 (DJ_WAppDelegate.m:108)
6 com.apple.Foundation 0x965b08d4 __NSFireTimer + 141
7 com.apple.CoreFoundation 0x922ffadb __CFRunLoopRun + 8059
8 com.apple.CoreFoundation 0x922fd464 CFRunLoopRunSpecific + 452
9 com.apple.CoreFoundation 0x922fd291 CFRunLoopRunInMode + 97
10 com.apple.HIToolbox 0x91646e04 RunCurrentEventLoopInMode + 392
11 com.apple.HIToolbox 0x91646bb9 ReceiveNextEventCommon + 354
12 com.apple.HIToolbox 0x91646a3e BlockUntilNextEventMatchingListInMode + 81
13 com.apple.AppKit 0x9265378d _DPSNextEvent + 847
14 com.apple.AppKit 0x92652fce -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 156
15 com.apple.AppKit 0x92615247 -[NSApplication run] + 821
16 com.apple.AppKit 0x9260d2d9 NSApplicationMain + 574
17 com.who.DJ-W 0x00001c92 start + 54
hope this helps... to find the error
EDIT 2:
With the Zombie Mode on I get this: *** -[CFString respondsToSelector:]: message sent to deallocated instance 0x46d550 I hope this helps.
EDIT 3:
To Ball: Here is the original URL (took away the domain here) /DJW/work.php?user=iBlackbirdi&udid=00000000-0000-1000-80005&key=660e5744e&cmd=slocau&type=get
There are several problems here:
djwLog is a class method so you should call it on the class not the instance
like this: [[self class] djwLog:#"foo bar"]
The URLFromString: method needs the string to contain a valid URL as specified by RFC 1808. Something along the lines of [NSURL URLFromString:#"http://example.com/foo"]
stringWithContentsOfURL:url… is a syncornous method. Unless you have this code running in a separate thread you should not use this. Look at NSURLConnection for a class to asynchronously load data from a URL. Using synchronized calls for this is a bad idea. Always.
intValue returns a signed integer. To get a NSInteger use integerValue. Also if you use stringWithContentsOfURL make sure to check if it's result is nil before calling
integerValue otherwise you might get a result of 0 if the URL call failed or did not return data. A real parser for the API you are calling would be a good idea in any case.
You're calling a class method as if it was an instance method:
[self djwLog:#"Logging in..."];
Since there is no instance-method named djwLog the application crashes. You should either call NSLog directly, make it an instance-method - or preferably make a macro for logging:
#ifdef DEBUG
# define DLog(...) NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
#else
# define DLog(...) do { } while (0)
#endif
If you instead of NSLog write DLog - it will only be logging when DEBUG-flag has been defined. In addition to this, it will log the origin of the log message - so you can see which class/method the log entry came from.

Mac Screensaver with WebView crashes

Hy everybody,
I have a screensaver made with obj-c and cocoa. Everything works fine under OsX 10.6.2 except the following.
Within my screensaver I have a WebView with some application running. When I try to call my objective-c app (the screensaver) via javascript, I get an error and the screensaver and the system preferences panel crash.
System Preferences[86666]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException'
reason: '-[NSCFArray drain]: unrecognized selector sent to instance 0x20049b1e0'
*** Call stack at first throw:(
0 CoreFoundation 0x00007fff8123a444 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff81f130f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff812931c0 +[NSObject(NSObject) doesNotRecognizeSelector:] + 0
3 CoreFoundation 0x00007fff8120d08f forwarding + 751
4 CoreFoundation 0x00007fff812091d8 _CF_forwarding_prep_0 + 232
5 WebCore 0x00007fff847adee0 _ZN3JSC8Bindings12ObjcInstance10virtualEndEv + 48
6 WebCore 0x00007fff8470d71d _ZN3JSC16RuntimeObjectImp18getOwnPropertySlotEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 397
7 JavaScriptCore 0x00007fff80862b66 NK3JSC7JSValue3getEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 486
)
I know this looks like some memory leak, but as you will see in the code, I really have nearly no objects allocated.
This only happens, when I start the screensaver with the "Test" button from the screensaver system prefs.
When I start the screensaver via terminal or if it starts automatically, the same action (calling obj-c from javascript) works fine.
Maybe someone has any idea, where the error could come from. Here is some code from the implementation:
#implementation ScreensaverView
- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview {
self = [super initWithFrame:frame isPreview:isPreview];
if (self) {
[self setAnimationTimeInterval:-1];
[self setAutoresizesSubviews:YES];
// ::::::::::::::::::::::: Init stuff ::::::::::::::::::
// init
quitFlag = false;
previewMode = isPreview;
// find out the path the screensaver bundle
pMainBundle = [NSBundle bundleForClass:[self class]];
pBundlePath = [pMainBundle bundlePath];
// read Info.plist
infoDict = [pMainBundle infoDictionary];
}
return self;
}
- (void)startAnimation
{
[super startAnimation];
// combine: bundle path + filename for screensaver file
NSString *pathToScreensaver = [NSString stringWithString:pBundlePath];
NSString *valueScreensaverFile;
if(!previewMode)
{
valueScreensaverFile = [infoDict objectForKey:#"ScreensaverFile"];
}
else
{
valueScreensaverFile = [infoDict objectForKey:#"PreviewFile"];
}
// add filename to bundle path
pathToScreensaver = [pathToScreensaver stringByAppendingString:valueScreensaverFile];
// complete NSURL to the screensaver file
NSURL *screensaverUrl = [NSURL fileURLWithPath: pathToScreensaver];
webView = [WebView alloc];
[webView initWithFrame:[self frame]];
[webView setDrawsBackground:NO];
// delegation policy for interactive mode
[webView setPolicyDelegate: self];
[webView setUIDelegate:self];
// load screensaver
[[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:screensaverUrl]];
scriptObject = [webView windowScriptObject];
[scriptObject setValue:self forKey:#"screensaver"];
[self addSubview:webView];
}
- (void)stopAnimation
{
[[webView mainFrame] stopLoading];
[webView removeFromSuperview];
[webView release];
[super stopAnimation];
}
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector
{
if (selector == #selector(quitScreenSaver)) {
return NO;
}
if(selector == #selector(gotoUrl:) ){
return NO;
}
return YES;
}
+(NSString *)webScriptNameForSelector:(SEL)selector
{
if(selector == #selector(quitScreenSaver))
{
return #"quitNoOpen";
}
if(selector == #selector(gotoUrl:))
{
return #"openAndQuit";
}
return nil;
}
- (void) quitScreenSaver
{
quitFlag = true;
[super stopAnimation];
}
- (void) gotoUrl:(NSString *) destinationURL
{
if(destinationURL == NULL)
{
return;
}
NSString * path = destinationURL;
NSURL * fileURL = [NSURL URLWithString:path];
[[ NSWorkspace sharedWorkspace ] openURL:fileURL];
[self quitScreenSaver];
}
#end
I hope that's enough code for you to see some problems / solutions.
I would really appreciaty any answers.
Somehow an NSCFArray (NSMutableArray) is being sent a "drain" message that's meant for an NSAutoreleasePool.
You might be able to get a bit more info on what the array is by implementing the drain method for NSMutableArray, so you can trap the now-recognized selector and print out the contents of the array object. Try adding this somewhere in your code:
#interface NSMutableArray (drain)
- (void) drain;
#end
#implementation NSMutableArray (drain)
- (void) drain
{
NSLog(#"drain message received by object: %#", self);
}
#end
If you don't see any messages show up in the Console, try changing the "NSMutableArray" in the above code to "NSObject".
One thing to be aware of is that when you start the screensaver via the "Test" button in System Prefs, you actually have 2 instances of your screensaver view running in the same process' address space on different threads. One (with isPreview==YES) is the little preview in the SysPrefs window (which continues running even when the full-screen version is started), and the other one is the full-screen version. They are both running in the SysPrefs.app process. So, you have to be careful to check all Notifications/etc. to see if they are coming from the view instance you expect.
I don't see any obvious problems from a quick glance at the code you posted, but it may be somewhere else. Do you use Notifications anywhere?
I put a similar webview-in-a-screensaver project on github at http://github.com/kelan/WikiWalker, where I initially had some similar problems (though I wasn't using any javascript stuff). It's not perfect code, but might help. I also did some tricks to forward notifications to the main thread (for drawing) in a . See the "Threaded Notification Support" parts of WWScreenSaverView.{h,m}.
Something to try:
Open up a terminal window and enter the following line to run System Preferences with NSZombieEnabled:
env NSZombieEnabled=YES "/Applications/System Preferences.app/Contents/MacOS/System Preferences"
Perform the steps that lead to the crash.
Run the Console app, set the filter in the upper right to "System Preferences", and look for NSZombie messages.
Hope this helps!
Just to troubleshoot, did you try not releasing the WebView?
Also, maybe set the WebView's delegates to nil before releasing it first?