problems with calling method of ViewController before calling viewController - ios

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.

Related

UIButton on XIB visible in parent view but IBAction not getting called

So I am trying to add a view as a subview onto an exisiting View Controller by loading it from an XIB in such manner -
- (void)showInView:(UIView *)aView animated:(BOOL)animated
{
self.view.frame = aView.frame;
dispatch_async(dispatch_get_main_queue(), ^{
[aView addSubview:self.view];
if (animated) {
[self showAnimation];
}
});
}
I see the button on the parent View Controller and it gets pressed too with the little animation that is there on UIButtons by default. The problem is that the IBAction connected to the UIButton never gets called.
Thanks in advance.
Adding the view as a class variable instead of a local variable did it for me.

Load Image In Container View On ViewDidLoad

I have a question regarding view hierarchy:
I currently have a HomeViewController that is loaded into view after a login screen. The HomeViewController has a ContainerView, which is embedded with a SubViewController. This container is initially hidden when the home view is loaded. When I trigger an action (via a UIButton) to "unhide" the container, I am trying to have an image load in that SubViewController that was rendered in the HomeViewController during viewDidLoad.
The problem I am facing is that for whatever reason, I can not seem to have the image display in the SubViewController.
FYI: The way I am rendering this image is such:
Upon the HomeViewController's viewDidLoad, I am taking a screen shot programmatically of a UIView that is generated from saved user data. This UIView is displayed in the view of the HomeViewController.
When the user clicks a button, I am unhiding and now showing the SubViewController, the view embedded in the ContainerView.
I can not seem to get the screen shot image (saved as imageSS) to appear in the SubViewController. I have tried to suppress the viewDidLoad method for the SubViewController when the HomeViewController is loaded, then call the SubViewController viewDidLoad when the button is pressed to show it, but no luck there either.
This might be confusing but it should be easy - I am at a loss why I can't get this to work. If anyone can help me out I would appreciate it. Thanks!
Here is the code i am currently trying: this method is in the HomeViewController.m
- (void)takeScreenshot:(id)sender {
UIGraphicsBeginImageContextWithOptions(_homeRenderImage.bounds.size, NO, 0.0);
[_homeRenderImage drawViewHierarchyInRect:_homeRenderImage.bounds afterScreenUpdates:YES];
UIImage *imageSS = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
SubViewController *pvc = [[SubViewController alloc] init];
userImage = imageSS;
[pvc loadImage];
}
Here is the method I am using in the SubViewController.m:
NOTE: userImage is a shared property between these controllers. I have tested that this sharing works
- (void)loadImage {
_subImage.image = userImage;
}
I feel dumb - all I needed to do was to call the loadImage method in the viewWillAppear method:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear: animated];
[self loadImage];
}

UIViewController: detecting when view appears again

I have a UIViewController class that contains a WKWebView and implements WKNavigationDelegate.
I would like to detect when a the view controller appears again. I understand the method loadView but, if I push a new view on the stack and then go back from that view to the previous view (my view controller) which method is called on the view controller?
The method that will be called is viewWillAppear:.
If you push to next view then viewDidLoad will be called first
Then viewWillAppear, viewDidAppear
If you pop to previous screen again (your UIViewController) then
viewWillAppear will be called first and after entire view appears
then viewDidAppear will be called..
viewDidAppear is useful in the cases where any method called at viewWillAppear after that you can Load the data at ViewDidAppear..
The ViewControllers viewDidLoad method is only called once when the view is created for the first time.
// viewDidLoad is called only once when the view is created for the first time
- (void) viewDidLoad
{
[super viewDidLoad];
// do your code here
}
You can also implement the below two methods in side your ViewController.m class
// viewWillAppear is called just before the view is about to be appeared
- (void) viewWillAppear
{
[super viewWillAppear];
// do your code here
}
// is called when the view has appeared
- (void) viewDidAppear
{
[super viewDidAppear];
// do your code here
}

Segmented control to switch views within view controller [duplicate]

This problem is driving me crazy. I'm trying to change the viewController when the user changes the selected "tab" of the segmented control. I've spent a couple hours researching and haven't been able to find an answer that works or is done through storyboard.
It really bother me since setting a tab application is so easy, but trying to use the segmented control like the tab application is just not working. I already know how to detect which index is selected in the segmented control. How can I achieve this?
Thank you very much.
NOTE: Answer updated with view controller containment code for iOS 5+ including #interface section
In an app of mine, I have a view controller with a Segment Control in the Navigation Bar and clicking on the "tabs" switches view controllers. The basic idea is to have an array of view controllers and switch between them using the Segment Index (and the indexDidChangeForSegmentedControl IBAction.
Example code (iOS 5 or later) from my app (this is for 2 view controllers but it's trivially extended to multiple view controllers); the code is slightly longer than for iOS 4 but will keep the object graph intact. Also, it uses ARC:
#interface MyViewController ()
// Segmented control to switch view controllers
#property (weak, nonatomic) IBOutlet UISegmentedControl *switchViewControllers;
// Array of view controllers to switch between
#property (nonatomic, copy) NSArray *allViewControllers;
// Currently selected view controller
#property (nonatomic, strong) UIViewController *currentViewController;
#end
#implementation UpdateScoreViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// Create the score view controller
ViewControllerA *vcA = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerA"];
// Create the penalty view controller
ViewControllerB *vcB = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerB"];
// Add A and B view controllers to the array
self.allViewControllers = [[NSArray alloc] initWithObjects:vcA, vcB, nil];
// Ensure a view controller is loaded
self.switchViewControllers.selectedSegmentIndex = 0;
[self cycleFromViewController:self.currentViewController toViewController:[self.allViewControllers objectAtIndex:self.switchViewControllers.selectedSegmentIndex]];
}
#pragma mark - View controller switching and saving
- (void)cycleFromViewController:(UIViewController*)oldVC toViewController:(UIViewController*)newVC {
// Do nothing if we are attempting to swap to the same view controller
if (newVC == oldVC) return;
// Check the newVC is non-nil otherwise expect a crash: NSInvalidArgumentException
if (newVC) {
// Set the new view controller frame (in this case to be the size of the available screen bounds)
// Calulate any other frame animations here (e.g. for the oldVC)
newVC.view.frame = CGRectMake(CGRectGetMinX(self.view.bounds), CGRectGetMinY(self.view.bounds), CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
// Check the oldVC is non-nil otherwise expect a crash: NSInvalidArgumentException
if (oldVC) {
// Start both the view controller transitions
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
// Swap the view controllers
// No frame animations in this code but these would go in the animations block
[self transitionFromViewController:oldVC
toViewController:newVC
duration:0.25
options:UIViewAnimationOptionLayoutSubviews
animations:^{}
completion:^(BOOL finished) {
// Finish both the view controller transitions
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
// Store a reference to the current controller
self.currentViewController = newVC;
}];
} else {
// Otherwise we are adding a view controller for the first time
// Start the view controller transition
[self addChildViewController:newVC];
// Add the new view controller view to the ciew hierarchy
[self.view addSubview:newVC.view];
// End the view controller transition
[newVC didMoveToParentViewController:self];
// Store a reference to the current controller
self.currentViewController = newVC;
}
}
}
- (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl *)sender {
NSUInteger index = sender.selectedSegmentIndex;
if (UISegmentedControlNoSegment != index) {
UIViewController *incomingViewController = [self.allViewControllers objectAtIndex:index];
[self cycleFromViewController:self.currentViewController toViewController:incomingViewController];
}
}
#end
Original example (iOS 4 or before):
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// Create the score view controller
AddHandScoreViewController *score = [self.storyboard instantiateViewControllerWithIdentifier:#"AddHandScore"];
// Create the penalty view controller
AddHandPenaltyViewController *penalty = [self.storyboard instantiateViewControllerWithIdentifier:#"AddHandPenalty"];
// Add Score and Penalty view controllers to the array
self.allViewControllers = [[NSArray alloc] initWithObjects:score, penalty, nil];
// Ensure the Score controller is loaded
self.switchViewControllers.selectedSegmentIndex = 0;
[self switchToController:[self.allViewControllers objectAtIndex:self.switchViewControllers.selectedSegmentIndex]];
}
#pragma mark - View controller switching and saving
- (void)switchToController:(UIViewController *)newVC
{
if (newVC) {
// Do nothing if we are in the same controller
if (newVC == self.currentViewController) return;
// Remove the current controller if we are loaded and shown
if([self.currentViewController isViewLoaded]) [self.currentViewController.view removeFromSuperview];
// Resize the new view controller
newVC.view.frame = CGRectMake(CGRectGetMinX(self.view.bounds), CGRectGetMinY(self.view.bounds), CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
// Add the new controller
[self.view addSubview:newVC.view];
// Store a reference to the current controller
self.currentViewController = newVC;
}
}
- (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl *)sender {
NSUInteger index = sender.selectedSegmentIndex;
if (UISegmentedControlNoSegment != index) {
UIViewController *incomingViewController = [self.allViewControllers objectAtIndex:index];
[self switchToController:incomingViewController];
}
}
I'd say it's much simpler to change subviews within a UIViewController, you can set up your subviews in your storyboards and hook them up with IBOulets in your controller you can set the hidden property of your views to YES or NO depending on the control that was clicked.
Now, if you use #Robotic Cat's approach which is also a good solution you can have a little more modularity in how your app works, considering you'd have to place all your logic in one controller using the solution I presented.
UISegmentedControl is a little different in that it doesn't have a delegate protocol, you have to use the "add target" style. In your case what you want to do is add a target to be notified when the UISegmentedControl changes (which is likely the parent view controller), and then that target can deal with the tab switching.
For example:
[self.mainSegmentedControl addTarget:self action:#selector(changedSegmentedControl:) forControlEvents:UIControlEventValueChanged];
In this example, the code is being invoked from some view/controller that has access to the variable for the segmented control. We add ourself to get the changedSegmentedControl: method invoked.
Then you would have another method like so:
- (void)changedSegmentedControl:(id)sender
{
UISegmentedControl *ctl = sender;
NSLog(#"Changed value of segmented control to %d", ctl.selectedSegmentIndex);
// Code to change View Controller goes here
}
Note: this is untested code written from memory -- please consult the docs accordingly.
Take a look at this pod: https://github.com/xmartlabs/XLMailBoxContainer. It makes the UI animation among the view controllers. These view controller can extend UITableViewController or any other view controller.
I hope this help you!

Pushing self.view to navigation controller by allocing, but when coming back same data is shown

I am pushing self view to self.navigationcontroller by allocating. I have a tableView on that view so I am changing the content of tableview. But when I am pressing back button (that is automatically created), I am not able to show previous content. Its showing updated content.
Please suggest.
You set code in viewWillAppear method
- (void)viewWillAppear:(BOOL)animated
{
//code set here
}
If you fill the tableView's data in viewWillAppear: or viewDidAppear:, it will reload even if you only press the back button of your top viewController. If you do not want to have your content changed, you are supposed to use initWithNibName: or viewDidLoad: methoads. They are called only at creation time of the view.
Based on the comments on #Kirti's post, You can check if your viewcontroller is being popped by following method, and take some necessary actions for you controller holding table.
-(void) viewWillDisappear:(BOOL)animated
{
if(![self.navigationController.viewControllers containsObject:self])
{
YourControllerWithTable *instance = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count - 1];
instance.loadOldContent = YES;
}
}
In viewWillAppear: of YourControllerWithTable, you can check:
-(void) viewWillAppear:(BOOL)animated
{
if(loadOldContent)
{
//Do your work here
}
}
You don't push UIView instances onto a UINavigationController instance, only instances of UIViewController.

Resources