I have two views, both are UIViewControllers, the first one contains a tableview with a button that segues to a camera view which is the second one.
So The first ViewController has been done in storyboard, including the segue to the second ViewController which is done by a "Show". The second ViewController has been done mostly in code, I have a UIImageView which I have added a selector method too which I want to use to navigate back to the first ViewController when pressed.
How do I do this in code as currently the UIImageView overlay I have created for the second ViewController which presents the camera view entirely in code.
Currently this is what I have created in code to initiate the method
//.....
UIImage * myImage = [UIImage imageNamed: #"Avatar_Mel"];
UIImageView *avatar = [[UIImageView alloc] initWithImage: myImage];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(mySequeMethod)];
avatar.userInteractionEnabled = YES;
[avatar addGestureRecognizer:tap];
//.....
- (void)mySequeMethod {
// what should I do here? How to I show the first view controller again?
}
If second controller is modally presented
[self dismissViewControllerAnimated:YES completion:nil];
In case of navigation stack
[self.navigationController popViewControllerAnimated:YES];
Related
I am trying to build a simple library with working UIElements. What I am trying to do is, creating UIViewController objects from one class instances and push that new ViewController on the current VC Stack with the presentViewController method.
I can see that the UIElements has been successfully adding on the stack, but GestureRecognizer and UIButton's target does not work. When I am checking on ViewDebug, these settings are <NSNull null>.
This is my class method which I am creating the UI and putting on the current view stack.
-(void)displayAd{
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:fullpageCampaign.mainImage]];
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
fullPageView = [[UIViewController alloc] init];
fullPageView.view.frame = CurrentVC.view.bounds;
fullPageView.view.backgroundColor = [UIColor redColor];
UIImageView *staticImageView = [[UIImageView alloc] init];
staticImageView.frame = CurrentVC.view.frame;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected)];
singleTap.numberOfTapsRequired = 1;
[staticImageView addGestureRecognizer:singleTap];
staticImageView.userInteractionEnabled = YES;
[fullPageView.view addSubview:staticImageView];
staticImageView.image = [UIImage imageWithData:imageData];
[CurrentVC.view addSubview:fullPageView.view];
//[fullPageView didMoveToParentViewController:self];
[CurrentVC presentViewController:fullPageView animated:YES completion:^{
NSLog(#"Tagon Ads is about to showing.");
UIButton *closeButton = [self createButtonWithAssetName:#"tagonAssets.bundle/close_button" TargetMethod:#"closeModal" andView:staticImageView];
[staticImageView addSubview:closeButton];
[CurrentVC.view bringSubviewToFront:closeButton];
}];
});
});
}
CurrentVC is the current viewController that I am sending as a parameter through my library's method in order to add a new viewController stack on to it.
Where is closeModal action? Probably same class as your currentVC. If so, your closeButton referenced currentVC but you already gone to fullPageView from there. So, your button lost his reference.
Just create new controller, send imageData there, create custom initializer, create new UIImageView and UIButton in there. With this way, your button gonna be reference own root and your problem should be solved.
There are several problems with your code, but first of all, I would recommend a different approach to accomplish what you want. As you can see below, using an instance of UIViewController is not the recommended way. Instead, use a storyboard to set up your view controller and it's components. Your code will be much smaller and your design will be easy to understand and change.
You can read more about UIViewController here
You rarely create instances of the UIViewController class directly.
Instead, you create instances of UIViewController subclasses and use
those objects to provide the specific behaviors and visual appearances
that you need.
Here is another potential. Is fullpageCampaign.mainImage residing remotely or locally? If remotely located, then you should consider changing to NSURLSession instead.
Read more about NSData:dataWithContentsOfURL here
Do not use this synchronous method to request network-based URLs. For
network-based URLs, this method can block the current thread for tens
of seconds on a slow network, resulting in a poor user experience, and
in iOS, may cause your app to be terminated.
Another minor thing is that you add the button to the image view. While this is OK, and might work when you allow user interaction for the image view, a cleaner way to do it is to create a UIView container to hold the image view and the button. The container can then also be the view that you attach the tap gesture recognizer to. That way, the image view can stay as a pure image.
The storyboard approach
First, create a sub-class of UIIViewController. It should look something like this:
FullPageViewController.h
#import <UIKit/UIKit.h>
#interface FullPageViewController : UIViewController
- (void)setImage:(UIImage *)adImage;
#end
FullPageViewController.m
#import "FullPageViewController.h"
#interface FullPageViewController ()
#property (weak, nonatomic) IBOutlet UIImageView *adImageView;
#end
#implementation FullPageViewController
- (void)setImage:(UIImage *)adImage {
self.adImageView.image = adImage;
}
- (IBAction)tappedOnAd:(UIGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
// Do your ad thing here
}
}
- (IBAction)closeButtonPressed:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Second, create a storyboard and add your ad view controller to it. Then add an image view, a button, and a tap gesture recognizer to your view controller. The tap gesture recognizer should be dropped on the image view to capture taps from there. You pull all of these objects from the Object Library down right. Also remember to enable user interaction for the image view. There is a property for that on the property page.
You should now have something that looks like this:
Notice the class name top right which should be the name of your new view controller class you just created. Also notice the storyboard ID adVC which you need when instantiating the view controller from code.
The next step is to connect the objects. Select the image view, then drag from the outlet (the ring) under Referencing Outlets to the view controller icon (the yellow icon with a square in) located on top of the view controller window, and select adImageView. The gesture recognizer should already be connected, if you dropped it on the image view when you placed it previously.
Next, connect the action for the close button. Drag from the Touch Up Inside outlet to the view controller icon (the yellow one), and select the closeButtonPressed: method.
Next, connect the tap gesture recognizer to your code. Select it from the list on the left, then drag from Sent Actions to the view controller icon and select tappedOnAd:.
Finally, your code to show the ad looks something like this. This method belongs in your parent view controller.
-(void)displayAd{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:fullpageCampaign.mainImage]];
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"AdPage" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"adVC"];
[vc setImage:[UIImage imageWithData:imageData]];
[self presentViewController:vc animated:YES completion:^{}];
});
});
}
I want to show a controller as pop-up on click of a button. Suppose I have VC1 that has button, then on click of that the VC2 should load as a pop up. It is working to some extent. But I don't' know for what reason the VC2 is loading twice and the second time it loads it shows black background. Here is my code:
On click of button below function gets called in VC1,
VC2* childVC = [[VC2 alloc]init];
childVC.view.hidden = YES;
[self addChildViewController:childVC];
[self.view addSubview:childVC.view];
[self performSegueWithIdentifier:#"goToVC2" sender:self];
In VC2 viewdidload,
self.view.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5];
How can I make VC2 appear as a pop up properly?
You don't need to segue and add the child view controller's view to the current view controller's view hierarchy. You should do one or the other. I would suggest the segue as that is a more apple approved approach. So change this:
VC2* childVC = [[VC2 alloc]init];
childVC.view.hidden = YES;
[self addChildViewController:childVC];
[self.view addSubview:childVC.view];
[self performSegueWithIdentifier:#"goToVC2" sender:self];
to
[self performSegueWithIdentifier:#"goToVC2" sender:self];
I have two view controllers in Xcode project (all view controllers are created in storyboard).
First view controller has two (or more) buttons with certain background images. Second view controller should display full-screen background image of certain button after user touch it (certain button).
Second view controller has a property UIImageView that should be allocated and initialized in the code of second view controller (UIImageView not created in storyboard).
Second view controller is a delegate for first view controller and has a method:
-(void) viewController:(ViewController *) viewController buttonPressed: (UIButton *) button.
Every button has a modal segue to second view controller.
So the sequence of actions of application is next (I realized that by debugging):
User touches any button
Button calls an action method in which delegate method viewController:buttonpressed: is called. UIImageView instance is allocated and initialized in this method with the image returned by button backgroundImageForState:
Than method viewDidLoad of second view controller is called, in which UIImageView instance should be added to super view of second view controller and displayed on screen.
The problem is that despite of allocation of UIImageView instance in delegate method viewController:buttonpressed:, that instance is become nil at the start of method viewDidLoad of second view controller. All actions that been made in viewController:buttonpressed: became unavailing.
The code is below:
First View Controller Code
- (void)viewDidLoad {
[super viewDidLoad];
ViewControllerForImage *temp = (ViewControllerForImage *) [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControlForImage"];
self.delegate = temp;
}
-(IBAction)buttonPressed:(id)sender{
[self.delegate viewController:self buttonPressed:sender];
}
Second View Controller Code
#synthesize myImage;
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:myImage];
}
-(void) viewController:(ViewController *) viewController buttonPressed: (UIButton *) button{
myImage = [[UIImageView alloc] initWithImage:[button backgroundImageForState: UIControlStateNormal]];
[myImage setFrame:CGRectMake(0, 0, 320, 504)];
}
Why don't you just pass the information in a prepareForSegueMethod?
Also in your code why are you sending an instance of viewcontroller back to the second view controller? You are not using it at all.
I have a TabBarController having 3 viewcontrollers in it (namely VCa, VCb, VCc) and at VCa the tabbar is set as hidden.
Now whenever we tap(using UITapGestureRecognizer) the view at VCa, what should be the selector method to be implemented in order to view another ViewController (either VCb or VCc).
"At viewdidLoad of VCa"
[self.tabBarController.tabBar setHidden:YES];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapmethod)];
[self.view addGestureRecognizer:tap];
-(void)tap{
// code to be written in order to show VCb(or any other ViewController in same TabBarController)
}
Everything is explained in the docs : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarController_Class/#//apple_ref/occ/instp/UITabBarController/selectedViewController
In short :
- (void)tap {
self.tabBarController.selectedIndex = 1;
// or
self.tabBarController.selectedViewController = self.tabBarController.viewControllers[1];
}
The correct tab bar item will be selected.
Set the selectedIndex of UITabBarViewController to the VC that you want to show.
I'm trying to create a transition between two scenes, this is a dumbed down version of what I have in my production code :
Both are ViewController, the left one has a TableView inside it and when clicked it should transition to the right hand scene, passing along data from whatever cell was clicked.
Currently, with a modal segue I can tap the cell and it transitions correctly, however, I can't figure out how to place a back button onto the nav bar.
I'm transitioning from the cell to the 2nd view controller like so :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toSecond" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toSecond"])
{
NSLog(#"Preparing segue for toSecond, setting some data on target scene");
NSIndexPath *path = [self.theTableView indexPathForSelectedRow];
MyData * myData = [myDataArray objectAtIndex:path.row];
// Obtain handles on the current and destination controllers
FirstController * startingViewController;
SecondController * destinationController;
startingViewController = (FirstController * ) segue.sourceViewController;
destinationController = (SecondController * ) segue.destinationViewController;
destinationController.someData = myData;
}
}
On the SecondController, I've tried amending the viewDidLoad method to programatically include a back button item as suggested in this previous SO question:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIBarButtonItem * back = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStylePlain
target:nil
action:nil];
[self.navigationItem setBackBarButtonItem:back];
}
So my question is, how can I get a back button onto that nav bar? Something like this :
Thanks
One (easy) way, using the storyboard, is to embed a navigation controller into your view that has the table. Make sure the correct view, the one with the table, is highlighted and then go to Editor > Embed In > Navigation Controller.
The back button will automatically be there when you click a row of your table. Like Dan said, make sure it is a push segue between the 2 views in your picture.
You will not be able to add a back looking button to a modally presented ViewController easily
If you just want to add a normal button to the left side of the bar, do the following
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonSystemItemDone target:nil action:nil];
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:#"Title"];
item.leftBarButtonItem = rightButton;
item.hidesBackButton = YES;
[self.yourNavigationBar pushNavigationItem:item animated:NO];
If you insist of presenting your view modally and still want a back button style you could use
Three20
In place of where you have the view with the table, us a UINavigationController don't delete the view with the table, just in place of where you segue to it, or if it was the root controller, use the nav con. Make the view with the table the root view controller of the navigation controller, and then simply use a push segue instead of a modal segue, and you should automatically get the back button
The best way is to just use a UINavigationController as the parent of your table view controller, and use a push segue instead of a modal segue.
You can create two instances of UINavigationItem and tell the UINavigationBar about them by setting the bar's items property. Instance 0 represents the table view controller.
You can create a UIButton with type 101 (the undocumented back button type), and wrap it in a UIBarButtonItem using initWithCustomView:.