sharing objects between flipside view and main controller view xcode - xcode

Our student team is currently working on a iphone app and we are stuck on a section of our program where the user can customise how long the timers in our app run for by inputting the value on the flipside view using a UItextfield. We cant figure out how to update these values in the main view controller from the flipside view controller any help would be greatly appreciated we have looked through a lot of examples and nothing seems to work.
//Inside the FlipsideViewController where the user changes the values in a UITextField
- (IBAction)update:(id)sender {
MainViewController *viewController = [[MainViewController alloc]
InitWithNibName:#"MainViewController" bundle:nil];
viewController.ward1Time = [ward1.text intValue];
viewController.ward2Time = [ward2.text intValue];
viewController.ward3Time = [ward3.text intValue];
viewController.ward4Time = [ward4.text intValue];
}
//Inside the MainViewController where we want to take the input the user put in in the FlipsideViewController
- (IBAction)newGreenWard:(id)sender {
[myWardObject setCurrentTime:ward1Time];
}

Related

The new UISplitViewController in iOS8 using objective c without storyboard

I try to implement adaptive UI in my app. By making UISplitViewController as the rootview controller, I can run the iPhone's code in iPad too.
I red Apple's documentation about UISplitViewController and some samples. All are using storyboards and the sample codes are available in swift only. I can not find a working version of code. So I started the code myself.
See my splitview controller class (BaseSplitViewController)
BaseSplitViewController.h:
#import <UIKit/UIKit.h>
#interface BaseSplitViewController : UISplitViewController <UISplitViewControllerDelegate>
#end
BaseSplitViewController.m:
#import "BaseSplitViewController.h"
#import "TabBarViewController.h"
#interface BaseSplitViewController ()
#property(nonatomic, strong) TabBarViewController *primaryTabBarVC;
#property(nonatomic, strong) UINavigationController *primaryNavigationController;
#property(nonatomic, strong) UINavigationController *secondaryNavigationController;
#end
#implementation BaseSplitViewController
- (instancetype)init
{
self = [super init];
if (self)
{
[self setViewControllers:#[self.primaryNavigationController, self.secondaryNavigationController]];
self.delegate = self;
self.preferredDisplayMode = UISplitViewControllerDisplayModeAutomatic;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cellTapped:) name:#"cellTapped" object:nil];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignPrimaryViewController];
}
- (void)assignPrimaryViewController
{
// Need to assign tab bar controller as primary view controller here
}
- (void)assignSecondaryViewController:(UIViewController *)vc
{
// Need to update the secondary controller each time the primary controller was tapped
}
- (UINavigationController *)primaryNavigationController
{
if (!_primaryNavigationController)
{
_primaryNavigationController = [[UINavigationController alloc] init];
}
return _primaryNavigationController;
}
- (UINavigationController *)secondaryNavigationController
{
if (!_secondaryNavigationController)
{
_secondaryNavigationController = [[UINavigationController alloc] init];
}
return _secondaryNavigationController;
}
- (UITabBarController *)primaryTabBarVC
{
if (!_primaryTabBarVC)
{
_primaryTabBarVC = [[TabBarViewController alloc] init];
}
return _primaryTabBarVC;
}
#end
Some points:
The above class "BaseSplitViewController" is the rootview controller of my app.
That is, self.window.rootViewController = [[BaseSplitViewController alloc] init];
From Apple's Documentation,
"When designing your split view interface, it is best to install
primary and secondary view controllers that do not change. A common
technique is to install navigation controllers in both positions and
then push and pop new content as needed. Having these types of anchor
view controllers makes it easier to focus on your content and let the
split view controller apply its default behavior to the overall
interface."
So, I created two navigation controllers (primary/secondary) and set them as split view controllers's primary & secondary views. setViewControllers: can be used for this.
My primary view here is, tab bar view. So, inside the assignPrimaryViewController: method, I should assign my TabBarViewController as split view controller's primary view.
Here, I found two ways.
1. [self.primaryNavigationController showViewController:self.primaryTabBarVC sender:nil];
2. [self.primaryNavigationController pushViewController:self.primaryTabBarVC animated:YES];
Here, I tried with [self showViewController:self.primaryTabBarVC sender:nil]; but my tab bar view was never shown. From my understanding, here "self" means the UISplitViewController. Calling showViewController: here makes the confusion to choose the navigation controller. Because we have two navigation controllers. So we need to clearly tell that navigation controller which needs to hold the primary controller.
Primary view controller part is over. Now the real problem starts. Consider my primary view controller is the tab bar which have tableview's in it. If I tap on the cell, I need to update the secondary view's content. This is the case in Regular mode. In compact mode, I expect when the user taps on the cell, it should push the detail view (secondary view) with back button.
I expect to put the below code within assignSecondaryViewController: vc: method
[self.secondaryNavigationController pushViewController:vc animated:NO];
[self.primaryNavigationController showDetailViewController:self.secondaryNavigationController sender:nil];
But it does not works.
Questions:
What should be placed inside assignPrimaryViewController & assignSecondaryViewController: methods to get my expected result?
And I really, yes really don't know how to implement UISplitViewController's following delegate methods.
primaryViewControllerForCollapsingSplitViewController:
splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
primaryViewControllerForExpandingSplitViewController:
splitViewController:separateSecondaryViewControllerFromPrimaryViewController:
Would be really helpful, if someone explains this new UISplitViewController's behavior.
Thanks

Passing data from detail to master in ios6

I'm making an application with xcode, and I have a trouble with passing data between views.
I want to set in detail view, a date of a calendar. Then when I go back to the master view I want to see the events in the selected date, but I don`t know how I make it.
Can you help me?
this is how you can communicate between 2 class
ViewController *dvController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:[NSBundle mainBundle]];
// ViewController is controler where you want to send date from particular controler we are making object of ViewController where you want to send data
dvController.selectedBirthdate = selectedbirthdate;
dvController.selectedName = selectedname;
//you are sending data in above two lines just imagine you are sending string
[self.navigationController pushViewController:dvController animated:YES];
//then pushviewcontroller and there you go
[dvController release];
simple as that
there is another way to comunicate between 2 classes is that app delegate make object of you app delegate and then assing what you want to particular varible of app delegate and then you can use that variable anywhere in project
create app delegate object like this
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
//and then access the variable by appDelegate.variable
if you are using storyboard then you can use prepareForSegue like below
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:...]) {
MyViewController *controller = (MyViewController *)segue.destinationViewController;
controller.myProperty1 = ...;
controller.myProperty2 = ...;
}
}
There are basically two most common approaches:
1) use unwind segue if you're using storyboard in the project. The approach is perfectly discussed here:
http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/SecondiOSAppTutorial/CreatingAddView/CreatingAddView.html
2) use delegate pattern. I found the below tutorial quite useful when I had started to learn delegation:
http://www.roostersoftstudios.com/2011/04/12/simple-delegate-tutorial-for-ios-development/

ipad uisplitview: passing data from masterviewcontroller to a uiviewcontroller (redirected via segue)

I'm new to iOS programming so I want to make this thing from scratch to be able understand how the whole thing works. So instead of using the master detail template I did this from the ground up.
I'm having a huge roadblock in terms of passing data between the master view and the detail view. At the moment, whenever I tap on the item of the master view it would look for the first view on the detail view controller, let's call it mainswitchviewcontroller. It's the first ViewController connected via segue to the UINavigationController for the detail controller part (Please refer to image below).
This happens after login where it shows that viewcontroller, I want the user to pick an option before accessing the whole app. which will lead them to the last ViewController of the flow (refer to the image below - red circle).
Now what I'm trying to do is this:
#interface MasterListViewController : UITableViewController{
}
#property (nonatomic) int matNum;
#property (strong, nonatomic) DetailStaffMainContentViewController *detailViewController;
#end
:
:
#implementation MasterListViewController
#synthesize matNum;
:
:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
for(UIViewController *vc in self.splitViewController.viewControllers){
NSLog(#"%#)", vc.title);
}
self.detailViewController.teststring = #"gronk";
}
This is my detailViewController (the one I want to access)
#implementation DetailStaffMainContentViewController
#synthesize tableView;
#synthesize teststring;
#synthesize lblOutput;
Now on select of the row, I assign the teststring which I want to use to populate that label on the controller. That's what I did.
My questions are:
I'm getting an error. Wondering what I was doing wrong?
[MainSwitchBoardViewController setTeststring:]: unrecognized selector
sent to instance 0x7572190
Is this a recommended way of passing data? Or should I just stick with notificationcenters?
Thoughts?
EDIT
Just to give you on the flow of the app:
User logs in (if username does not exist, the floating viewcontroller will pop up and force the user to log in.
Once the user logs in, the pop up goes away and then asks the user what type of role is to be used in the current session (eg. staff or fighter).
If the user picks fighter, it pushes the user to the top VC If the user picks staff, it pushes the user to the bottom VC (collections controller)
FOR Staff: At the moment I'm pushing to the last VC with this [self perfermSegueWithIdentifier:sender:self]
Once the user gets shown the last VC (DetailsStaffView), I want this to be the main Details View where the user does it's transaction. A few more pop ups? (Or maybe another push to another VC for specific transaction needs to be added). But ultimately I don't want the user to access the first Viewcontroller (MasterSwitchBoard)
EDIT 2
This is the main switch board class that controls the first VC after login.
#interface MainSwitchBoardViewController : UIViewController{
}
#property (strong, nonatomic) IBOutlet UILabel *txtLabel;
- (IBAction)btnStaff:(id)sender;
- (IBAction)btnAdmin:(id)sender;
#end
This is the collections VC which atm automatcially pushes to the last VC. In the beginning I was using a notification on this but it seems like it's not necessary so I took it out.
#interface MatListCollectionViewController ()
#end
#implementation MatListCollectionViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self performSegueWithIdentifier:#"SeguePushToStaffDetailFromMats" sender:self];
}
And then the details staff
#interface DetailStaffMainContentViewController : UIViewController{
}
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property (nonatomic, retain) IBOutlet UILabel *lblOutput;
#property (nonatomic, retain) NSString *teststring;
#end
#implementation DetailStaffMainContentViewController
#synthesize tableView;
#synthesize teststring;
#synthesize lblOutput;
I'll tackle those in reverse order:
Is this a recommended way of passing data? Or should I just stick with notificationcenters?
You theoretically could use notification center, but I wouldn't in this case. Personally, I only use NSNotificationCenter if:
I'm doing asynchronous process which has no reasonable way of knowing the state of the view controllers (e.g. I have some background task that's doing some time consuming update of data from a server, and the user could have navigated to just about anywhere in the app by the time I'm ready to notify the view controllers that the update is done); and/or
I potentially have multiple objects that I want to notify of some event.
Neither of those conditions apply here. Certainly you could use NSNotificationCenter, but that should be unnecessary. Explicitly setting properties is generally preferable.
I'm getting an error. Wondering what I was doing wrong?
Unfortunately, it's hard to say on the basis of the evidence you've presented thus far. But there are a few things that I'm having trouble reconciling on the basis of your code and comments:
You've declared detailViewController to be a DetailStaffMainContentViewController (and that's the class for which you've synthesized the teststring accessors);
But your error message suggests that detailViewController is clearly a MainSwitchBoardViewController object (which doesn't understand the setTeststring accessor method); and
You haven't shown us how you're setting detailViewController, so it's hard to say where you've gone wrong here.
Personally, in cases like this, I'm always reticent to define properties to maintain pointers to view controllers. I'd generally be inclined to ask the UISplitViewController what's in the detail split. So, rather than having a class property/ivar for that, in my master view controller, I have a method like:
- (UIViewController *)detailViewController
{
return [[self.splitViewController.viewControllers lastObject] topViewController];
}
That's basically saying "get the last object from my split view controller's array of viewControllers (the detail split) and because I (personally) always use a navigation controller in that detail split, let's use the topViewController to get a pointer to my subclassed UIViewController that's in that detail split (embedded within the navigation controller).
Then I have my didSelectRowAtIndexPath check to see if that view controller is the one I expected. If not, I segue to it (and have prepareSegue pass the data I want to that controller).
So, for example, consider this storyboard:
Here, "A" is my default detail controller. "B" might be some random scene that I may have replaced "A" with. And "C" is my real detail view controller where I can see the details for the item I selected from the master view controller's table view. Thus, my didSelectRowAtIndexPath for the master view controller might look like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *controller = [self detailViewController];
if (![controller isKindOfClass:[DetailCViewController class]])
{
// if I'm not already showing "C" in my detail split, then let's segue to it
[self performSegueWithIdentifier:#"GoToC" sender:self];
}
else
{
// if I'm already showing "C" in my detail split, let's just set the item of data I want to pass to it
DetailCViewController *cController = (DetailCViewController *)controller;
cController.detailItem = self.objects[indexPath.row];
}
// I have a method I call to make sure that the popover menu for the master view
// controller disappears (in case it popped up since I was in portrait mode) and
// adds the master view controller button to the navigation bar if I need it.
[self removePopoverAndAddNavigationButton];
}
In case I needed to segue to "C", I also have my `prepareSegue pass the detailItem as needed:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"GoToC"])
{
DetailCViewController *cController = (DetailCViewController *)[segue.destinationViewController topViewController];
cController.detailItem = self.objects[[[self.tableView indexPathForSelectedRow] row]];
}
}
As I said, trying to figure out why your detailViewController has the wrong type of object is hard to say, but this is how I might tackle something like this.
Maybe you should first take a look at some nice movies from the last WWDC where they describe Storyboard Segues.
In the code you provided there is nothing written about segues. Be sure to use segues either via IB or programmatically via [self performSegueWithIdentifier:]. See Apple Docs
Then you can provide additional operations in yout view controller:
[view shouldPerformSegueWithIdentifier:sender:]
[view prepareForSegue:sender:]
The first one decides whether to perform the segue or not. The second one is executed just before segue is performed. Within the secon operation you can provide information to the destinationViewController.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"showIssuesByProject"]){
if (_searchActive) {
((IssueListViewController *)segue.destinationViewController).project = [_filteredProjects projectAtIndex:[self.tableView indexPathForSelectedRow].row];
} else {
((IssueListViewController *)segue.destinationViewController).project = [_projects projectAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
if ([segue.identifier isEqualToString:#"showIssuesByFilter"]) {
((IssueListViewController *)segue.destinationViewController).filter = [_filters filterAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
Be careful: UISplitViewControll.viewControllers are only two controllers: the master and detail ones.

Xcode best way to re-use area/view for multiple forms

I'm new to Xcode and starting a universal app. In the iPad version I want to re-use the main section for different search forms.
I want the user to see a completely different form in the section to the right (see image) for each button on the left menu.
I'm working programmatically.
What's the best way to change the content in this situation? (keeping in mind performance and coding complexity)
EDIT: This is a standard tabbed application, not a split view. Could I have a new view controller and .xib for each form and then change which one is displayed by embedding them in a UIView or UIWindow on this screen?
You've hit upon the right answer in your edit. From what I understand the buttons on the side are analagous to another tab bar controller, but nested within your main tab bar controller?
You might as well follow the same pattern, since it is a familiar one. Your search view controller should act as a container view controller, which would have an array of view controllers representing each form option.
As you switch between the options, add/remove the appropriate view controller views from your view hierarchy (using addSubview) and view controller hierarchy (using the code in "Adding and removing a child" in the link). The view controller hierarchy is important to ensure that viewDidAppear and so forth is called on your child controllers.
As an illustration, I've created a simple demo project where the main view controller has a set of buttons, each linked to the same action, and an array of contained view controllers. The contained view controllers will be held inside a subview, called container in this example. This would be the size of the right hand area in your screenshot above.
The child controllers are set up as follows in viewDidLoad of the container view controller:
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *buttons;
#property (weak, nonatomic) IBOutlet UIView *container;
#property (nonatomic,strong) NSArray *viewControllers;
#property (nonatomic, strong) UIViewController *currentChild;
#end
#implementation JRTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
for (UIButton *button in self.buttons)
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
NSMutableArray *children = [NSMutableArray new];
for (int i = 0; i < 4; i++)
{
ContainedViewController *child = [ContainedViewController new];
child.name = [NSString stringWithFormat:#"Form %d",i + 1];
[children addObject:child];
}
self.viewControllers = [children copy];
[self buttonTapped:self.buttons[0]];
}
Here I've used four instances of the same view controller class - all it has is a label which indicates which form you are selecting. Really you'd have different classes for each one. I've also "selected" the initial view controller by sending the action method for the first button. The action method does this:
- (IBAction)buttonTapped:(UIButton *)sender
{
NSUInteger index = [self.buttons indexOfObject:sender];
if (index != NSNotFound)
self.currentChild = self.viewControllers[index];
}
Which selects the appropriate VC from the view controller array. The setter method does this:
-(void)setCurrentChild:(UIViewController *)currentChild
{
if (currentChild == _currentChild) return;
// Remove the old child
[_currentChild willMoveToParentViewController:nil];
[_currentChild.view removeFromSuperview];
[_currentChild removeFromParentViewController];
[_currentChild didMoveToParentViewController:nil];
// Add the new one
[currentChild willMoveToParentViewController:self];
[self.container addSubview:currentChild.view];
NSDictionary *views = #{#"view":currentChild.view};
[self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[view]|" options:0 metrics:nil views:views]];
[self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[view]|" options:0 metrics:nil views:views]];
[self addChildViewController:currentChild];
[currentChild didMoveToParentViewController:self];
_currentChild = currentChild;
}
Which sets up the view controller hierarchy, adds in the view and uses constraints to make it fill the container.
In this example I've hardcoded in four buttons and four child view controllers as I was demonstrating the addition and switching of child view controllers. In reality you'd make it more like a tab bar controller where you assign an array of view controllers and the container would make its own array of buttons.

Xcode 4.3.3 : how to assign a view controller while initializing

Hy all.
Since i am using IOS 5 , therefore i am using storyboard.
In the older versions, i could easily write initWithNibName:#"Details", and it worked like a charm.
Now in storyboard, and since i am not using any XIB file, i need to do the same thing.
Here's a snippet of my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the selected country
NSString *selectedAuthors = [theauthors objectAtIndex:indexPath.row];
//Initialize the detail view controller and display it.
Details *dvController = [[Details alloc] initWithNibName:#"Details" bundle:nil];
dvController.selectedAuthors = selectedAuthors;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}
My new Snippet :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the selected country
NSString *selectedAuthors = [theauthors objectAtIndex:indexPath.row];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard.storyboard" bundle:nil];
Details *dvController = [storyboard instantiateViewControllerWithIdentifier:#"Details"]; //Or whatever identifier you have defined in your storyboard
//Initialize the detail view controller and display it.
//Details *dvController = [[Details alloc] init/*WithNibName:#"Details" bundle:nil*/];
dvController.selectedAuthors = selectedAuthors;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}
Application Log :
2012-07-17 16:30:15.760 AuthorsApp[6534:f803] WARNING: Using legacy cell layout due to delegate implementation of tableView:accessoryTypeForRowWithIndexPath: in <AuthorVC: 0x6857ba0>. Please remove your implementation of this method and set the cell properties accessoryType and/or editingAccessoryType to move to the new cell layout behavior. This method will no longer be called in a future release.
2012-07-17 16:30:16.167 AuthorsApp[6534:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Storyboard (<UIStoryboard: 0x6867750>) doesn't contain a view controller with identifier 'Details''
*** First throw call stack:
(0x1595022 0x1726cd6 0x500fef 0x3151 0x1675c5 0x1677fa 0x9fc85d 0x1569936 0x15693d7 0x14cc790 0x14cbd84 0x14cbc9b 0x147e7d8 0x147e88a 0xd6626 0x1dc2 0x1d35)
terminate called throwing an exception(lldb)
Latest Application Log:
2012-07-17 16:35:15.352 AuthorsApp[6600:f803] WARNING: Using legacy cell layout due to delegate implementation of tableView:accessoryTypeForRowWithIndexPath: in <AuthorVC: 0x688d810>. Please remove your implementation of this method and set the cell properties accessoryType and/or editingAccessoryType to move to the new cell layout behavior. This method will no longer be called in a future release.
2012-07-17 16:35:15.912 AuthorsApp[6600:f803] Everything is ok now !
(lldb)
Final Log
Couldn't register com.test.erc.AuthorsApp with the bootstrap server. Error: unknown error code.
This generally means that another instance of this process was already running or is hung in the debugger.(lldb)
You could instantiate the controller that is located in your storyboard like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"myStoryboard" bundle:nil];
Details *dvController = [storyboard instantiateViewControllerWithIdentifier:#"Details"]; //Or whatever identifier you have defined in your storyboard
Another option since you're using Storyboards would be using segues for the transitions. Here is a nice example. One thing that you get for free with segues is that the destination controller gets instantiated automatically for you. The delegate method looks like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"Details"]){
Details *dvController = (Details *)[segue destinationViewController];
// Pass any data to destination controller
// The transition is handled by the segue and defined in the Storyboard ('push' for example)
}
}
EDIT
Here is a screenshot for easy reference:

Resources