UIViewcontroller not reloading after notification from observer - objective-c

Hi I have a uiviewcontroller that adds 4 childviews after receiving data from a webservice. I add an observer to the uiviewcontroller that watches when the data is completely loaded from the webservice. Once it is, the uiviewcontroler adds 4 childviews.
however my problem is it doesn't do this unless i switch to another tab view than go back to that tab again.
Is there a way to reload this page? I used [uiviewcontroller.view setNeedsDisplay] under the function that responds to the notification from the observer but the view doesn't refresh.
thanks for your guys' help :)
here is my observer function:
-(void)projectListFromServer:(NSNotification *) response
{
NSLog(#"%#",response);
for (int i = 0; i < 4; i++)
{
childview2 = [self.storyboard instantiateViewControllerWithIdentifier:(#"slider")];
childview2.image = #"wicked";
childview2.couponID = #"cool";
NSLog(#"%#", self.childview2.couponID);
[self addChildViewController:childview2];
}
[self.childview.view setNeedsDisplay];
}

You are calling addChildViewController, but then it looks like you aren't actually adding childview2's view hierarchy to this containing view controller's view hierarchy.
You'll need to do something like this, adjusted of course depending on where in your current hierarchy you actually want these new views to be inserted:
[self addChildViewController:self.childview2]; // assuming childview2 is a #property
[self.view addSubview:self.childview2.view]; // adjust based on your actual hierarchy
[self.childview2 didMoveToParentViewController:self];
BTW, you should avoid giving view controllers names like childview2. Views and view controllers are two completely different things.

Related

UIPageViewController within NavigationController

I've read every tutorial I've found about UIPageViewController, but they show just basics, I'd like to create something like new twitter app has:
UIPageViewController is embedded into Navigation controller, title of navigation bar is based on current page and those page dots are there as well, user can tap on item on current page(item from table view/collection view) to see detail.
I was able to come up with something similar, each page had collection view, and showing detail of some item was reflected in navigation bar, there was correct title and "<" button, but I wasn't able to change title based on currently shown page
Please, could you describe me how to do this in few steps/basic structure of controllers?
Don't know if you are still working on this but here goes anyway. To set up a UIPageViewController you might use the tutorial and two questions below.
http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/
How to implement UIPageViewController that utilizes multiple ViewControllers
How to add UIBarButtonItem to NavigationBar while using UIPageViewController
The last link pertains specifically to setting the contents of the navigationBar depending on what you are viewing.
The key is to create a UINavigationItem Property in the .h file of your UIPageViewController content view controllers, meaning the ones/one that are displaying whatever it is you are displaying.
From my code in FirstViewController.h SecondViewController.h and ThirdViewController.h
#property (strong, nonatomic) UINavigationItem *navItem;
In the second and third links above you'll see a storyboard layout of a Master-Detail application (which uses a navigation controller). The UIPageViewControllerDataSource is the DetailViewController. The three pages associated with the pageViewController are my content view controllers.
In DetailViewController.m you have to instantiate the contentViewControllers somewhere. At that point you pass the DetailViewControllers navigationItem id to the content view controllers. Here is how I instantiate my content view controllers using the delegate methods of the UIPageViewController.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSString * ident = viewController.restorationIdentifier;
NSUInteger index = [_vc indexOfObject:ident];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
if (index == 0) {
return [self controllerAtIndex:index];
}else if (index == 1){
return [self secondControllerAtIndex:index];
}else if (index == 2){
return [self thirdControllerAtIndex:index];
}else{
return nil;
}
}
The delegate method calls the method below. It is almost directly from the tutorial link with just a few modifications.
-(FirstController *)controllerAtIndex:(NSUInteger)index
{
FirstController *fvc = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstPageController"];
fvc.imageFile = self.pageImages[index];
fvc.titleText = self.pageTitles[index];
fvc.pageIndex = index;
fvc.navItem = self.navigationItem;
return fvc;
}
Notice that properties are passed into the view controller including self.navigationItem. Passing it in ensures you can make changes to the navigationBar items.
Then in the viewDidAppear method of your content view controller you can simply set the title on the navigation bar like this.
navItem.navigationItem.title = #"Whatever you want the title to be";
It is important to use viewDidAppear because viewDidLoad is not called every time the screen appears. I believe the UIPageViewController caches the page before and the page after whatever you are viewing which saves the system from having to load the page every time you navigate to it.
If you are using a single view controller for all you pages like the tutorial does you will have to use the index property to know what to set the title to.
Hope this helps!
I had a very similar setup and solved the problem.
My setup is that I have a UIPageViewController inside a UINavigationController because I wanted the navigation bar to be persistent while I swiped between each view controller. I wanted the title of the current UIViewController inside the UIPageViewController to become the title of the UINavigationController.
The way to do this is to implement the UIPageViewControllerDelegate method pageViewController didFinishAnimating which triggers after a change to a view controller is made from the UIPageViewController. You can probably see where this going: From here, set the navigationItem.title property of the UIViewPageController, which the UINavigationController uses to set it's own title, with that of the current view controller's title.
Example:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
if(finished && completed)
{
NSString *titleOfIncomingViewController = [[pageViewController.viewControllers firstObject] title];
pageViewController.navigationItem.title = titleOfIncomingViewController;
}
}
NB: This delegate method triggers only off gesture-initiated scrolls.
Solution given by Mr. Micnguyen is the exact solution but the mentioned delegate method didFinishAnimating() is called when swipe action is done due to which initially title is not shown.
Hence to resolve that problem, we need to set its initial value in viewDidLoad() method of UIPageViewController class as mentioned below:
- (void)viewDidLoad(){
self.navigationitem.title = arrayname[0];
}
I had this question too, but ended up doing something different, because I'm using Swift, so I thought I'd share here.
I ended up embedding a UIPageViewController in a Container View in my Navigation Controller. On a page swipe, I used pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:), with my PageViewController as the UIPageViewDelegate. From there I created a protocol that I used to send data about the VC displayed to the parent VC, and change the title using self.title = "My Title".
I didn't make the parent VC the UIPageViewDelegate because I found it to be easier to access the displayed VC from the PageViewController than from the parent VC as let childVC = pageViewController.viewControllers![0] as! DetailViewController.

How to implement more than one controller in objective C

First of all I don't know If controller is the right word. What I want to achieve is this.
#interface ClubViewController : CoreDataTableViewController :NRGridViewController
{
I know that this is not possible in objective-C. But is there a way to work around this?
Because I want to use CoreDateTableViewController and NRGridViewController.
Kind regards
Stef
EDIT
This is how my storyboard Hierarchy looks like.
-ViewController
-TableView
-View
-TableViewCell
So I have a tableview Controller but above this tableview controller you find a small view with three buttons. When I push on button 1 I want to take the tableview away and draw a gridView with the NRGridview Controller. But when I push on button 2 and 3 I fill up my tableview using the CoreDataTableViewController.
I hope this explains more my problem.
I think one way to do this is with a container view with a container view controller inside it. That container controller would have 2 child controllers which would be your CoreDateTableViewController and NRGridViewController. I've implemented something like this, and I can show you some code if you're interested.
After Edit: In a test app, I started with a single view template and a storyboard. I added two buttons to the top of the view and a container view to the bottom half of the view (this first controller is of class ViewController). I then dragged out a new view controller, and control dragged from the container view to the new controller and chose the "embed segue" (this will resize the view to be the same size as the container view). The class of this controller was changed to my subclass, ContainerController. I then created 2 more controllers for the 2 views that will be managed by the container controller (the views need to have their size set to "freeform" in IB so you can set the size to be the same as the container view). Here is the code in ContainerController:
- (void)viewDidLoad
{
[super viewDidLoad];
self.cont1 = [[FirstController alloc]initWithNibName:#"FirstView" bundle:nil];
self.cont2 = [[SecondController alloc]initWithNibName:#"SecondController" bundle:nil];
[self addChildViewController:self.cont1];
self.currentController = self.cont1;
[self.view addSubview:self.cont1.view];
}
-(void)switchToFirst {
if (self.currentController != self.cont1) {
[self addChildViewController:self.cont1];
[self moveToNewController:self.cont1];
}
}
-(void)switchToSecond {
if (self.currentController != self.cont2) {
[self addChildViewController:self.cont2];
[self moveToNewController:self.cont2];
}
}
-(void)moveToNewController:(id) newController {
[self.currentController willMoveToParentViewController:nil];
[self transitionFromViewController:self.currentController toViewController:newController duration:.6 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{}
completion:^(BOOL finished) {
[self.currentController removeFromParentViewController];
[newController didMoveToParentViewController:self];
self.currentController = newController;
}];
}
The only code I have in ViewController are the IBActions for the 2 buttons that switch the views. Those methods just call methods in the container controller:
-(IBAction)chooseFirstController:(id)sender {
[self.childViewControllers.lastObject switchToFirst];
}
-(IBAction)chooseSecondController:(id)sender {
[self.childViewControllers.lastObject switchToSecond];
}
What you are trying to do in your code is creating a class that is a subclass of multiple other classes, which is not possible. If you really want to do this, check out this question: Inherit from two classes
If you are trying to create multiple instances:
CoreDataTableViewController and NRGridViewController are just classes, which you will have to instantiate to get an actual object.
You can instantiate e.g. an NRGridViewController using
NRGridViewController *controller=[[NRGridViewController alloc] init];
I hope this answers your question, it is a bit difficult to understand your question.
Instead of taking tableViewController, take normal TableView (drag and drop from the storyboard to the particular position on the view).
when the button 1 is pressed Make the Table View hidden in the buttons action method. and initialize the grid view / or set grid view hidden to NO. (all views have the property of hidden in ios)
when you press on the 2nd and 3rd button set the grid view hidden and set tableview hidden equal to NO. and fetch the coredata and store it in array or dictionary or you can reload the tableview.
(Initially, before pressing the button 2 & 3 , the table view has no values. so you can set a bool property that when you press the button 2 or 3 set the bool and use the bool to reload your tabe view )
if you did not get my explanation ping me back.

Get current view

I have an app which has split view inside a tab bar, and these split views often have navigation hierarchy and then sometimes modal views are presents on top of them, and it all works fine, but...
I am trying to display a passcode lock whenever the app goes into background, so I put
[self.window.rootViewController presentModalViewController:lockView animated:YES];
in my AppDelegate's method
- (void)applicationWillResignActive:(UIApplication *)application
...which works fine unless a modal view is displayed.
the passcode does not display if a modal view is open.
Is there a way to retrieve the currently active view controller so I can present this lock view?
Thanks in advance
Cheerio
Code that worked was as follows:
BOOL hasKids = YES;
UIViewController *topViewController = (UIViewController*)[[(UITabBarController*)self.window.rootViewController viewControllers] objectAtIndex:((UITabBarController*)self.window.rootViewController).selectedIndex];
while (hasKids) {
if (topViewController.presentedViewController) {
hasKids = YES;
topViewController = topViewController.presentedViewController;
} else {
hasKids = NO;
}
}
[topViewController presentModalViewController:lockView animated:YES];`
I think the easiest way is to keep track of which tab is currently active (there are a number of ways to do this, but I'd recommend implementing the UITabBarControllerDelegate and handling its tabBarController:didSelectViewController: method).
Once that's done, you'll probably need to manage a property in each view controller that holds any modal view controllers you present. If, however, you're on iOS 5 or higher, look into the UIViewController property presentedViewController. It appears that this is a new way to do exactly what you want.

How to load a view controller using a segmentControl value change event?

I have three views each represented by a View controller named firstVC, secondVC and thirdVC. In the firstView (firstVC) I have a segmented control with three options first,second and third. When I click on the second segment control I want to load the corresponding view controller which in this case would be secondVC. I am new to View Controllers but I did try various options. I created an outlet and also a value changed event. I event tried pushing a view controller when the value changes but it pulls a blank page as opposed to secondVC.
Please see below for the code.
- (IBAction)chainAction:(id)sender {
UINavigationController *navcon = self.navigationController;
if (chainSegControl.selectedSegmentIndex == 1) {
secondVC *atSecondVCChain = [[secondVC alloc] init];
[navcon pushViewController:atSecondVCChain animated:YES];
}
}
Any help would be greatly appreciated. Thank You.
P.S I am using Xcode 4.2
Instead of using alloc to create a controller, define a named segue in your storyboard (if you don't already have one) that describes a push from firstVC to secondVC and then use the performSegueWithIdentifier:sender: method instead of pushViewController:.

Reload data from table view after come back from another view

I have a problem in my application. Any help will be greatly appreciated. Basically it is from view A to view B, and then come back from view B.
In the view A, it has dynamic data loaded in from the database, and display on the table view. In this page, it also has the edit button, not on the navigation bar. When user tabs the edit button, it goes to the view B, which shows the pick view. And user can make any changes in here. Once that is done, user tabs the back button on the navigation bar, it saves the changes into the NSUserDefaults, goes back to the view A by pop the view B.
When coming back to the view A, it should get the new data from the UIUserDefaults, and it did. I used NSLog to print out to the console and it shows the correct data. Also it should invoke the viewWillAppear: method to get the new data for the table view, but it didn't. It even did not call the tableView:numberOfRowsInSection: method. I placed a NSLog statement inside this method but didn't print out in the console.
As the result, the view A still has the old data. the only way to get the new data in the view A is to stop and start the application.
Both view A and view B are the subclass of UIViewController, with UITableViewDelegate and UITableViewDataSource.
Here is my code in the view A :
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"enter in Schedule2ViewController ...");
// load in data from database, and store into NSArray object
//[self.theTableView reloadData];
[self.theTableView setNeedsDisplay];
//[self.theTableView setNeedsLayout];
}
In here, the "theTableView" is a UITableView variable. And I try all three cases of "reloadData", "setNeedsDisplay", and "setNeedsLayout", but didn't seem to work.
In the view B, here is the method corresponding to the back button on the navigation bar.
- (void)viewDidLoad {
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:#selector(savePreference)];
self.navigationItem.leftBarButtonItem = saveButton;
[saveButton release];
}
- (IBAction) savePreference {
NSLog(#"save preference.");
// save data into the NSUSerDefaults
[self.navigationController popViewControllerAnimated:YES];
}
Am I doing in the right way? Or is there anything that I missed?
When a view is first loaded, it calls the viewDidLoad method. If you create a stack, drill down into it (from A to B) and then return (B to A) the viewDidLoad does not get called again on A. What you may want to try is passing A into B (by passing in self) and call the viewDidLoad method to get the new data and then reloadData method on the the tableView to refill the table view.
What you may want to try is taking the data fetching and setting functionality out of the viewDidLoad method and place it in its own getData method. At the end of the getData method, you could place a [self.tableView reloadData]; to reset/refill the table view. From class B, you could call [self getData] and minimize the amount of work you would do in class B. This would help increase reuse-ability of that code and may prevent side effects from calling the viewDidLoad method.
You could also use viewDidAppear. It is called every time the screen appears. For performance reasons, set a flag so you don't repeat the same functionality in viewDidLoad with viewDidAppear for the first screen view.