Declaring UITouchGestures? - objective-c

I'm working on using a UISwipeGestureRecognizerDirectionRight method to change views within an application and i have used the following code in the main file of the View Controller but it seems as though i need to define the gesture after the asterisk and declare it as this build error states:
"swipeGesture undeclared"
-(void)createGestureRecognizers {
UISwipeGestureRecognizerDirectionRight * swipeGesture = [[UISwipeGestureRecognizerDirectionRight alloc]
initWithTarget:self
action:#selector (handleSwipeGestureRight:)];
[self.theView addGestureRecognizer:swipeGesture];
[swipeGesture release];
}
-(IBAction)handleSwipeGestureRight {
NC2ViewController *second2 =[[NC2ViewController alloc] initWithNibName:#"NC2ViewController" bundle:nil];
second2.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second2 animated:YES];
[second2 release];
}
So my question is how do i declare the "swipeGesture" after the asterisk in the header file or have i done something wrong?
Thank You

UISwipeGestureRecognizerDirectionRight is an enum value for the four possible directions. It is not a class you instantiate to recognize gestures. Use UISwipeGestureRecognizer instead:
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector (handleSwipeGestureRight:)];
//Set the direction you want to detect by setting
//the recognizer's direction property...
//(the default is Right so don't really need it in this case)
swipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipeGesture];
[swipeGesture release];
Also, the handler method should be:
-(IBAction)handleSwipeGestureRight:(UISwipeGestureRecognizer *)swipeGesture {
because in the selector for the action, you put a colon in the method name meaning you want it to pass the sender object as the first parameter. (You could also remove the colon from the selector instead if you don't need the sender in the handler.)
Finally, void is more appropriate than IBAction in the handler since it won't be called from an object in a xib. However, since IBAction and void are the same thing, it won't cause a problem.

Related

GKScore challengeComposeControllerWithPlayers not showing view

Here's my code:
GKScore *scoreObj = [[[GKScore alloc] initWithCategory:category] autorelease];
scoreObj.value = playerScore;
[scoreObj
challengeComposeControllerWithPlayers:playerIDs
message:msg
completionHandler:^(UIViewController *composeController,
BOOL didIssueChallenge,
NSArray *sentPlayerIDs) {
// I don't really care what you do
}];
When I run the code, I get no overlay at all, and no error messages in the log.
Currently, playerIDs is an NSMutableArray that's empty. I don't know if that's related, but I don't want to have to pre-populate a list - that's what this native UI is suppoed to do for me, right? mgs is an empty string.
First of all in iOS 7 initWithCategory: is deprecated. Use initWithLeaderboardIdentifier: instead. As the documentation says:
Provides a challenge compose view controller with pre-selected player identifiers and a preformatted, player-editable message.
So save a pointer to this view controller, present it from your current view controller and dismiss it from within the completion handler.
Assuming you're within the implementation of your current viewController do the following:
GKScore *scoreObj = [[[GKScore alloc] initWithLeaderboardIdentifier:category] autorelease];
scoreObj.value = playerScore;
UIViewController *vc = [scoreObj challengeComposeControllerWithPlayers:playerIDs
message:msg
completionHandler:^(UIViewController *composeController,
BOOL didIssueChallenge,
NSArray *sentPlayerIDs) {
[composeController.presentingViewController dismissViewControllerAnimated: YES completion: nil];
// I don't really care what you do
}];
[self presentViewController: vc animated: YES completion: nil];
This should displayed the viewController as intended.

Reloading UITextView from another UIViewController

I have 2 view controllers MainDetailViewController (MD) and MainEditViewController (ME)
There is a textView in MD with some text already there when view loads. Then I call ME like this
MainEditViewController *editVC = [[MainEditViewController alloc] init];
editVC.theTextView.text = self.theTextView.text;
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:editVC];
[self.navigationController presentViewController:navCon animated:YES completion:nil];
I am passing the TextView's text value to ME like above and calling it with presentViewController method.
In ME I edit the text and click on save button which should update the text value in MD's textView
MainDetailViewController *mainDetailVC = [[MainDetailViewController alloc] init];
mainDetailVC.theTextView.text = self.theTextView.text;
[self dismissViewControllerAnimated:YES completion:nil];
This is not reflecting change in MD
What am I doing wrong?
The easiest way to do this is by using block.
Add new block typedef to your MainDetailViewController.h file just after imports but before #interface:
typedef void (^ReturnBlock)(NSString *arg);
In MainEditViewController.h file between #interface and #end add public property:
#property(copy) ReturnBlock returnBlock;
This is your block which will be called when you dismiss your ME view controller.
Next move to MainEditViewController.m file and replace code in save button from:
MainDetailViewController *mainDetailVC = [[MainDetailViewController alloc] init];
mainDetailVC.theTextView.text = self.theTextView.text;
[self dismissViewControllerAnimated:YES completion:nil];
to:
if (self.returnBlock)
self.returnBlock(self.theTextView.text);
[self.navigationController popViewControllerAnimated:YES];
You should use popViewControllerAnimated: method instead of dismissViewControllerAnimated.
The last change left to do is in your MainDetailViewController.m file when you create editVC, add after:
editVC.theTextView.text = self.theTextView.text;
this:
editVC.returnBlock = ^(NSString *returnText) {
NSLog(#"Returned text: %#", returnText);
self.theTextView.text = returnText;
};
Happy coding.
Views are not loaded until you present the view, so you will need to create a property to hold that string value and in viewDidLoad, you should set the textfield with the string value.
You need to use a protocol to retrieve data from B to A.
You can have a look here
and here.

UIViewController and UITableViewController, how to change self.view then back again?

I dont want to add a sub view, but instead change the "self.view" to another view eg (A warning view) then after the user suppresses the warning I would like to switch back. When ever i try to switch back to the original view i just get a blank screen for reasons i cant understand.
Here is what i currently have in one of my UITableViewControllers
//Show warning view controller
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view;
//Then later
self.view = self.tableView; //<< Dosnt work
If you want to change your view, and if the original view is defined/linked into XCode, you must retain it before changing self.view to another view. If not, the original view is released and using it back can cause bad things to happen.
Warning :
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view
is a bad bad call. Because you autorelease the controller but you use its view. So you get a view retained with a released controller after some time. Retain the controller and release it yourself when its view is not needed anymore.
Here's the better way to do what I think you're trying to do:
WarningViewController *warningViewController = [[WarningViewController alloc] initWithNibName:#"theRightNiborNil" bundle:nil];
[self presentModalViewController:warningViewController animated:YES];
// or if you don't need to support iOS4 any more:
[self presentViewController:warningViewController animated:YES completion:nil]
// and if you aren't using ARC yet, then [warningViewController release];
Then in your WarningViewController you want some action that calls:
[self dismissModalViewControllerAnimated:YES];
// or again if this is iOS5..
[self dismissModalViewControllerAnimated:YES completion:nil];
Hope that helps.

Can't access UIView in external method

I'm new to Objective-C, so the way I'm going about this might be ludicrous, but here goes:
I have a login form in my iPhone application. When the user has entered their credentials, they hit Done in the top right corner, which triggers an IBAction and a custom progress indicator pops up. I've created this indicator by using a class containing an instance method named showProgressIndicator. showProgressIndicator creates and returns a UIView, which I then add to my view like so:
ProgressIndicatorElement *ProgressIndicator = [[ProgressIndicatorElement alloc] init];
box = [ProgressIndicator showProgressIndicator];
[self.view addSubview:box];
I have of course declared box as a UIView in my header file. The progress indicator pops up beautifully and in the meantime I'm doing a behind-the-scenes URL request that, when finished, calls another method in my view controller named receivedServerResponse. Now, what I want to do is to remove the progress indicator, which is why I'm doing this:
- (void)receivedServerResponse {
[box removeFromSuperview];
}
But nothing happens at all. I'm not getting any errors or warnings, and the code is being highlighted just as if everything was running smoothly. I've tried retaining the indicator in my IBAction, but that doesn't help either.
Hope you can help out!
Updated:
Here is the showProgressIndicator method:
- (UIView *)showProgressIndicator {
box = [[UIView alloc] initWithFrame:CGRectMake(85, 190, 210, 140)];
box.backgroundColor = [UIColor colorWithRed:0.0 / 255 green:0.0 / 255 blue:0.0 / 255 alpha:.6];
box.layer.cornerRadius = 8;
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.layer.frame = CGRectMake((box.layer.bounds.size.width - spinner.layer.bounds.size.width)/2, 20, spinner.layer.bounds.size.width, spinner.layer.bounds.size.height);
[spinner startAnimating];
[box addSubview:spinner];
UILabel *titleInBox = [[UILabel alloc] initWithFrame:CGRectMake(0, 65, 150, 20)];
titleInBox.font = [UIFont boldSystemFontOfSize:16];
titleInBox.textColor = [UIColor whiteColor];
titleInBox.textAlignment = UITextAlignmentCenter;
titleInBox.backgroundColor = [UIColor clearColor];
titleInBox.text = #"Authorizing...";
[box addSubview:titleInBox];
return box;
}
Second update:
#Deepak just pointed out in the comments that I might be running two different instances of my view controller, which actually seems to be the case. In the external class that handles the aforementioned URL request, I get back to the view controller's receivedServerResponse method by doing this:
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *responseString = [request responseString];
SignInViewController *viewController = [[SignInViewController alloc] init];
[viewController receivedServerResponse];
}
Without spreading myself too thin (probably too late ;)), ASIHTTPRequest is set up so that if you call one method that performs an asynchronous URL request, a predefined method called requestFinished (above) is called, which is why I've had to call my view controller this way, because I can't access the returned value in an easier way (that I know of).
Creating a new instance of SignInViewController is not the correct way. It only seems correct to maintain a weak reference (assigned property) of the SignInViewController object. Say your class is RequestHandler.
#interface RequestHandler: [..] {
}
#property (nonatomic, assign) SignInViewController * signInViewController;
#end
#implementation RequestHandler
#synthesize signInViewController;
[..]
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *responseString = [request responseString];
[signInViewController receivedServerResponse];
}
#end
So when you're creating a RequestHandler object within the SignInViewController instance, you do,
RequestHandler * requestHandler = [[RequestHandler alloc] init];
requestHandler.signInViewController = self;
[..]
Note, you can also look at delegation and notifications.
I think part of the problem may be with memory management. If showProgressIndicator does not return an autoreleased object, try releasing box after adding it as a subview, like so:
[self.view addSubview:box];
[box release];
box may not disappear if box is not deallocated when removed from the superview.
My other recommendation is that instead of doing it the way you are doing, creating a view, adding it, and then trying to removing it, you might want to try adding box as a subview when the login view is created and setting its hidden property to YES then unhiding it later when necessary.
Based on your update: You have some memory management issues in showProgressIndicator. Whenever you alloc an object, you should release it. In this case, release all of your variables after adding them as subviews as I mentioned above. box however should be returned as an autoreleased object since showProgressIndicator does not know when it will need to be released. For that you should replace return box; with return [box autorelease];
You need to send the activity indicator a stopAnimating message when you want the animation to stop. There's no need to remove it from its superview; instead, simply make sure that its hidesWhenStopped property is set to YES.
How about adding box view on window in appDelgate? Give a tag to your boxView and in the remove method get the boxView back by using tag. For example if you give tag 99
- (void)receivedServerResponse {
UIView *box = [window viewWithTag:99];
[box removeFromSuperview];
}
also you don't need to declare an instance variable in header file. and you can access progress indicator anywhere in the application.
Without spreading myself too thin (probably too late ;)), ASIHTTPRequest is set up so that if you call one method that performs an asynchronous URL request, a predefined method called requestFinished (above) is called, which is why I've had to call my view controller this way, because I can't access the returned value in an easier way (that I know of).
ASIHTTPRequest calls -requestFinished: on the object you set as the request's delegate. You should design your classes such that this delegate object either has a reference to the view controller you want it to act on or has some means of notifying that view controller to take action.
The easiest solution might be to make the controller the request's delegate.

Image not being set in method

I have a class with a viewDidLoad method and an artworkInfo method as follows:
-(void)viewDidLoad {
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[super viewDidLoad];
}
-(void)artworkInfo:(NSNumber *)pos{
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[self.image setImage:(UIImage *)[[mainDelegate.mapAnnotations objectAtIndex:0]image]];
}
the mainDelegate thing is to gain access to the appDelegate where an array is stored, but anyway, with the "[self.image setImage...]" command where it is, the image on the app does not appear, but when I copy that exact line of code into the viewDidLoad method, it shows up like it should. I know that the artworkInfo method is being called because I debugged it and it goes through, so I can't figure out why the command would not be doing anything it's current method while it will in the viewDidLoad...?
Also, here is where the method is called and this new view is loaded from another class:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
infoPage *myInfoPage = [[infoPage alloc] init];
[myInfoPage artworkInfo:position];
[info release];
OH, I see the problem. You're instantiating 2 different infoPage classes.
Change this:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
infoPage *myInfoPage = [[infoPage alloc] init];
[myInfoPage artworkInfo:position];
[info release];
to this:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
[info artworkInfo:position];
[info release];
Ok detailed answer. In order to understand why this image is not displaying properly you have to first look at how Runloops work in Objective C.
While viewDidLoad is the method that is called when a view is loaded and it is technically also called before a view is displayed and it's view objects initialized. Since presentModalViewController is an animation there is actually some threading going on in the works.
viewDidLoad gets called before the animation is created for the presentModalView. This initializes your objects. However, due to some of the inner workings of UI Kit some processes are loaded off into a thread. When they complete they run callback methods on the main UI thread.
Since presentModalViewController is a non-blocking method your artworkInfo method gets added to the mainRunLoop before the initializer form thread adds its callback methods to the main run loop. The best approach would be to have both a UIImage property of your viewController and a UIImageView.
set the value of UIImage by calling artworkInfo BEFORE the presentModalViewController method.
in your ViewDidLoad go ahead and set the value of your UIImageView
[self.imageView setImage:self.image];
Problem solved.
This seems pretty straight forward.
So you initialize your nib and try to call your method artwork before the nib is fully loaded. <-- This is not working for you.
Then you do additional initialization by overrider viewDidLoad per the doco where the nib is loaded <-- This is working for you
So the answer is, when you call setImage before your nib is loaded, then there is nothing to set the image to. When you call setImage in viewDidLoad your nib is loaded and then things should work just fine.
I hope this explains it a bit.