Very new to iOS development. I'm trying to make an thing that will let me select a photo from the photo library, and display it. I'm using a storyboard with a navigation controller. I'm able to select and display the image just fine, but I can't figure out how to dismiss the imageview and return back to the image picker. I just get a fullscreen image and cant click anywhere to do anything. Code looks like this:
snapViewController.h
#import <UIKit/UIKit.h>
#interface snapViewController : UIViewController <UIImagePickerControllerDelegate,
UINavigationControllerDelegate>
#property (strong, nonatomic) IBOutlet UIImageView *ImageView;
- (IBAction) done:(UIStoryboardSegue *)unwindsegue;
#end
My .m
snapViewController.m
#import "snapViewController.h"
#interface snapViewController ()
#end
#implementation snapViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (BOOL)prefersStatusBarHidden {
return YES;
[self setNeedsStatusBarAppearanceUpdate];
}
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (self.ImageView.image == nil){
UIImagePickerController * imagePickerController = [UIImagePickerController new];
imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
imagePickerController.delegate = self;
[self presentViewController:imagePickerController animated:NO completion:nil];
}
else {
}
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];
self.ImageView.image = image;
[self.ImageView setUserInteractionEnabled:YES];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction) done:(UIStoryboardSegue *)unwindsegue {
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Storyboard looks like this, I stuck a toolbar on the bottom of the UIImageView with a done button:
Do I need to bind the done button to the [self dismissViewControllerAnimated:YES completion:nil]; action? How can I do this? I've been struggling with this for a couple hours and can't figure it out. Thanks.
Read up a bit on views and windows and view controllers and stuff, came up with this:
- (IBAction)kill:(id)sender {
self.ImageView.image = nil;
[self viewDidAppear:TRUE];
[self dismissViewControllerAnimated:NO completion:nil];
}
I bound my done button to it and it looks like it works. It seems to be acceptable memory wise(opening and closing images multiple times doesn't ever make memory increase over what it was originally). It if this is not the right way to do it please let me know. Thanks.
Modal segues are very special compared to other segues in the sense that when you dismiss the new view controller, you call a method from the old one. The way it works is like this:
In the old view controller create a method of type (IBAction*) with argument (UiStoryBoardSegue*). Example:
-(IBAction*)done:(UIStoryBoardSegue*)segue{}
Then, in the story board, from your "Done" button in the new view controller control-drag to the green (left most) button in the line on the bottom of the new view controller (same view controller as the Done button), the method you created in the old view controller will popup, and you need to select it.
What will happen now is that once the done button is pressed, the modal will dismiss and will call the Done method on the old view controller
You are on the right track with your kill IBAction however there's a few things you don't need in there. You should not be calling viewDidAppear from an action. Let the OS manage all calls to viewDidLoad, willAppear, didAppear, etc.
If you want to remove the imageview's image from displaying, then you simply need to call self.imageView.image = nil;. You could then add another button and hook it up to an action to open the image picker modal and select a different image.
Remember that snapViewController is not a modal, it is simply the root view controller in the navigation stack that happens to have a full-screen image on it. The Navigation controller is a component that holds a bunch of view controllers -- it itself is never displayed to the user. That said, you do not need to dismiss snapViewController to reset the image, you should just need a done or kill action that looks like this:
- (IBAction)kill:(id)sender {
self.imageView.image = nil;
}
Related
I am new to iOS development and I am doing little project as a research.
I have an app where after I start it I am showing MainViewController, but if this is the first launch of this app I want to show Sub1ViewController (names are made up) using presentViewController method called in MainViewController.
After user puts in some data on Sub1ViewController I invoke dismissViewController method to hide it.
The hard part starts here - I have no idea how to capture the event when Sub1ViewController is dismissed and I can present Sub2ViewController also using presentViewController invoked from MainViewController. All the time I am getting messages that I am trying to present view when another present or dismiss is in progress.
PS: I am using Xamarin, but I also understand objective-c.
hi you can try out this
[self dismissViewControllerAnimated:YES completion:^{
//code to be executed with the dismissal is completed
// for example, presenting a vc or performing a segue
}];
you can write code after completion of dismiss one view controller
or
You can achieve this by present view after some delay in dismiss method
-(void)onDismisViewController{
[self performSelector:#selector(presentSecoundViecontoller) withObject:self afterDelay:1];
}
-(void)presentSecoundViecontoller
{
SecoundViewController *secoundVC = [[SecoundViewController alloc] initWithNibName:#"secoundVC" bundle:nil];
[self.navigationController presentViewController:secoundVC animated:YES completion:NULL];
}
EDIT
Try this i Try and it work
crate one delegate Method in subviewcontroller1 and set delegate in mainviewcontroller and implement method in mainvie and present subviewcontroller2 in this delegate method its work
try this if not let me know will post code.
Delegate Creation on subviewcontroller1.h file
#protocol st1Protocol <NSObject>
- (void)presentViewController;
#end
#interface SubViewController1 : UIViewController
#property (nonatomic,assign) id <st1Protocol> delegate;
- (IBAction)dissmiss:(id)sender;
SubViewcontroller1.m file
i put dismiss view on button click of subviewcontroller1 you do this in you dismiss method
- (IBAction)dissmiss:(id)sender {
[self dismissViewControllerAnimated:YES completion:^{
//code to be executed with the dismissal is completed
// for example, presenting a vc or performing a segue
[self.delegate presentViewController];
}];
}
now implementation of this delegate method in main view controller.
implement delegate method in .h file
#interface StViewController : UIViewController<st1Protocol>
set delegate in mainviewcontroller.m file view didload
- (void)viewDidLoad
{
[super viewDidLoad];
subViewcontroller1 *st1=[[subViewcontroller1 alloc]initWithNibName:#"subViewcontroller1" bundle:nil];
st1.delegate=self;
[self presentViewController:st1 animated:YES completion:nil];
// Do any additional setup after loading the view from its nib.
}
delegate method implementation in main view controller
-(void)presentViewController{
SubmViewcontroller2 *st2=[[SubmViewcontroller2 alloc]initWithNibName:#"St2ViewController" bundle:nil];
[self presentViewController:st2 animated:YES completion:nil];
}
hope this may help you happy coding.
I have trouble w/ hiding toolbar when leave screen by segue.
Details: App has a few dozen screens, all of them are belonged the same navigation controller. A few screens have toolbar. For these a few screens in -(void)viewDidLoad I use
[self.navigationController setToolbarHidden:NO animated:NO];
and in -(void)viewWillDisappear:(BOOL)animated:
[self.navigationController setToolbarHidden:YES animated:YES];
so toolbar is visible only on necessary screens and the screen which needs toolbar controls the visibility. All work well when I navigate by "back" button.
The trouble starting when I try to navigate by segue like this (goto home & goto another branch).
[owner.navigationController popToRootViewControllerAnimated:NO];
[self performSegueWithIdentifier:SEGUE_NAME sender:self];
toolbar is stay visible in spite of calling -(void)viewWillDisappear which should hide toolbar.
are there any ideas how to perform these "ToolBarHidden" by right way.
thanks
PS: of course I could hide toolbar on every screen, but I want to avoid this unnecessary operations and want to know how to do it right.
**STEP1:** in your controller.h
#interface ViewController : UIViewController {
UIToolbar *bar;
}
#property (nonatomic, strong) IBOutlet UIToolbar *bar;
#end
**STEP2:** in your controller.m
#synthesize bar;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.bar.hidden = NO;
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
self.bar.hidden = YES;
}
**STEP3:** connect in Intrface
hope this help you!
Ive got a storyboard with 2 ViewControllers, the first ViewController got a button to pick a photo from the photo album. The second ViewController has a ImageView.
the outcome is a black screen.
This is the delegate method:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self dismissViewControllerAnimated:NO completion:nil];
UIImage * selectedIMG = [info objectForKey:UIImagePickerControllerOriginalImage];
VCImageEditingViewController *ieVC = [self.storyboard instantiateViewControllerWithIdentifier:#"VCImageEditingViewController"];
ieVC.selectedIMG = selectedIMG;
[self presentViewController: ieVC animated:NO completion:nil];
}
In the VCImageEditingViewController.m:
-(void)loadView
{
if (self.selectedIMG) {
self.imageDisplayed.image = self.selectedIMG;
}
else{
UIImage *image = [UIImage imageNamed:#"hat.jpg"];
self.imageDisplayed.image = image;
}
}
Even when i change the if statement to show the "hat" photo in both cases it shows me a black screen, so i'm pretty sure its got to do with the image picker.
Thank you for your help.
Two things:
You are using loadView when you should be using viewDidLoad and calling [super viewDidLoad]
The line: [self.view sendSubviewToBack:self.imageDisplayed]; doesn't display the image on the image view, it should be: self.imageDisplayed.image = self.selectedIMG;
I think the problem is that you are overriding loadView, for a controller you set up in the storyboard.
The docs say for loadView:
If you use Interface Builder to create your views and initialize the view controller, you must not override this method.
....
If you want to perform any additional initialization of your views, do so in the viewDidLoad method.
Try to override -(void) viewDidLoad instead, and set the image for the ImageView (assuming that you have set up self.imageDisplay up as a View Controller's view's subview) there.
First, have a look at this screenshot of my storyboard:
It is an application for a sound map. The user can either record a new field recording or chose an existing one from the library and upload them. The ViewController where the user has to add a title/description etc. (or modifies them when coming from the library) is presented modally (on the top right).
If the user choses to cancel this and to delete the recording, he shall return to the recording screen, if he comes from there, otherwiese to the library. If he choses to save/upload the recording, he shall return to the library, where the upload progress will be displayed.
How can I come back to the desired ViewController independently of the ParentViewController that I come from?
I thought about unwindSegue, but that doesn't work for my layout. Then I figured out a dirty workaround where I changed the selected tab of the TabBarController. But then I also want to set up the delegate correctly to pass some data.
Any thoughts are welcome!
I don't think changing the selectedIndex of the tab bar controller is a "dirty workaround" -- that's the way to do it, given your set up. It appears that you would want the EntryViewController (Library) to be the delegate of the EntryDetailViewController. If that's so, you could set up the delegate in the viewDidLoad method of the EntryDetailViewcontroller like this:
#import "DetailViewController.h"
#import "TableController.h"
#interface DetailViewController ()
#property (strong,nonatomic) UITabBarController *tbc;
#end
#implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tbc = (UITabBarController *)self.presentingViewController;
self.delegate = (TableController *)[(UINavigationController *)self.tbc.viewControllers[1] topViewController];
}
- (IBAction)saveAndUpload:(UIButton *)sender {
[self.tbc setSelectedIndex:1];
[self.delegate saveRecording:#"test recording"];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)delete:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
In my example, I just have two buttons to either save or cancel, and my TableController would be the same as your Library controller.
I'm pretty new to iOS dev, and I've been reading a ton on this problem, but still can't figure it out.
I have a button in an action sheet that is supposed to activate and present a slide-up modal that is a from/to date picking screen (it has its own controller DatePickerViewController. The action sheet is triggered by a button in the toolbar of a NavigationViewController's subview ("Map of Shows" view, top left button). Graphic shows the current storyboard relationships:
The code for this sequence looks like this:
// ShowsContainerController.m
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( buttonIndex == 1 ){
// 1. Activates from actionsheet
[(NavigationViewController *)self.parentViewController showDateSelect];
}
}
// NavigationViewController.m
// 2. fires up the datepicker view
-(void)showDateSelect
{
pickerView = [[DatePickerViewController alloc] init ];
[self presentViewController:pickerView animated:YES completion:nil];
}
// DatePickerViewController.m
// 3. Instantiation of this controller. Definitely fires nslog.
-(void)viewDidLoad
{
NSLog(#"Here");
}
Once "here", the screen just goes black. I assume this is because I'm not doing something right either with the instantiation of the date picker controller, or with the segue to it. All of the views in question are associated with their respective controllers in the storyboard configs. To further confuse the issue, I have a UITableViewController that I created for another screen, and just for shits and giggles, attempted to load that, and it worked fine. I then created another completely separate UIViewController, pointed it back to the controller files that control the current non-working one, and it bombs as well, so I'm thinking the issue are the header and main files for the non-working UIViewController. ed. scratch that last note; I created a completely new header, main file and NIB for the view and it still didn't work. I don't know what the hell the deal is.
Any and all help would be greatly appreciated.
Addendum
- (IBAction)showOptions:(id)sender
{
NSString *showTypes;
if( self.onlyShowPreferredEvents ){
showTypes = #"\u2713 Only shows that I'll like";
} else {
showTypes = #"Only shows that I'll like";
}
_showDisplayOptionsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Event Display Settings" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles: showTypes, #"Date Range", nil];
[_showDisplayOptionsActionSheet showFromTabBar:self.tabBarController.tabBar];
}
Per the comments.
Addendum 2
// All of DatePickerViewController.m
#import "DateRangeViewController.h"
#interface DateRangeViewController ()
#end
#implementation DateRangeViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
// All of DatePickerViewController.h
#import <UIKit/UIKit.h>
#interface DateRangeViewController : UIViewController
#end
I would disconnect the DatePickerViewController from the Nav controller and leave it as a stand alone VC.
Then get rid of showDateSelect and change clickedButtonAtIndex method to the following:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( buttonIndex == 1 )
{
pickerView = [[DatePickerViewController alloc] init ];
[self.navigationController pushViewController:pickerView animated:YES];
}
}
Here you are pushing the pickerView unto the Navigation controller stack. You will need to pop it to remove it.
you can also experiment with present/dismiss but I am not sure whether it would work on Nav Controller stack.
Update:
I am not sure why you are re-declaring the viewDidLoad in your DatePickerViewController.h file. Take that out.
Also in all view Did/Will Load/Appear methods, you need to start by calling super. So insert [super viewDidLoad] as a first line in your viewDidLoad method of DatePickerViewController.m
Figured it out. The problem is that when you're using a storyboard, like I am, and you're programmatically presenting a view, you need to begin by creating a storyboard object:
// "MainStoryboard_iPhone" is your .storyboard file's name
// [NSBundle mainBundle] returns the main storyboard bundle.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone"
bundle:[NSBundle mainBundle]];
Next, you instantiate the view that you want to present using that storyboard object:
// Give your view an identifier in the storyboard. That's how the storyboard object will find it.
// You should see it in the right panel options when you click on the view.
UIViewController *dateRangeController = [storyboard instantiateViewControllerWithIdentifier:#"dateRange"];
Present.
[self presentViewController:dateRangeController animated:YES completion:nil];
My mistake was in thinking that if you simply associated the view with the viewcontroller file (under the Class property in the config pane), that's all that was needed. But if you're using a storyboard, the UI process is expecting views to be instantiated through it, whether automagically or programmatically (presumably because more than one view can use the same view controller).