I have FirstViewController and SecondTableViewController. In SecondTableViewController.m, I create a cell in the cellForRow... method where the cell.textLabel.text is a string from an NSInteger property ("count") of the SecondTableViewController.
I would like a button in FirstViewController to increment the value of count.
I've tried making a property of FirstViewController and then using that:
#property SecondTableViewController *viewController;
and
- (IBAction)buttonTouched:(id)sender {
self.viewController.count++;
[self.viewController.tableView reloadData];
}
But this way isn't working. count is still its original value of zero. I've also reloaded the table in viewWillAppear and still nothing. How can I do this?
Count being used as a property may be where you are going wrong because count is a method that returns the number of objects in an array that is found in foundation framework. Also keep in mind that if you are storing a integer into a string object try storing it in this format.
cell.textlabel.text = [NSString stringWithFormat: #"%i", count];
Hope this helps
Try following
firstViewController.h
#interface DMFirstViewController : UIViewController
#property (nonatomic, strong) DMSecondViewController * secondController;
- (IBAction)buttonPressed:(id)sender;
#end
firstViewController.m
- (IBAction)buttonPressed:(id)sender
{
++self.secondController.count;
[self.navigationController pushViewController:self.secondController animated:YES];
}
secondViewController.h
#property (nonatomic) int count;
secondViewController.m
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"%d", self.count);
}
EDIT
Check out those two images and implement the similar logic and get the solution.
----- END OF NEW EDIT -----
OLD
I think you haven't assigned and allocated memory for SecondTableViewController reference i.e, self.viewController of FirstViewController in its viewDidLoad method i.e,
-(void) viewDidLoad //In FirstViewController
{
self.viewController = [[SecondTableViewController alloc] init];
}
and pushed the same reference on to the stack of navigationController after performing button taps to increase the count of count variable of SecondTableViewController.
If you are not clear, comment.
I am new to iOS XCode and am attempting to complete the initial example. Everything works as expected and in debugging with breakpoints I cannot seem to set a value from the Add To-Do Item to show up in Self.textField, so the program just skips each of these steps and I am back to the initial list. UPDATE: It definitely does not seem to pass this line of code,
if (sender != self.doneButton) return;
thus all the other code does not execute either. Is it something to do with the configuration of the Text Field in the story board? - Thanks!
In AddToDoItemViewController.m the value in the text field does not seem to come across and get stored in this code.
#import "AddToDoItemViewController.h"
#interface AddToDoItemViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property (weak, nonatomic) IBOutlet UITextField *doneButton;
#end
#implementation AddToDoItemViewController
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if (sender != self.doneButton) return;
if (self.textField.text.length > 0) {
self.toDoItem = [[ToDoItem alloc] init];
self.toDoItem.itemName = self.textField.text;
self.toDoItem.completed = NO;
}
}
In ToDoListTableViewController.m, the action just sees that item is NILL so it is done...
- (IBAction)unwindToList:(UIStoryboardSegue *)segue
{
AddToDoItemViewController *source = [segue sourceViewController];
ToDoItem *item = source.toDoItem;
if (item !=nil) {
[self.toDoItems addObject:item];
[self.tableView reloadData];
}
}
It's driving me crazy as I have everything else working fine and am learning how nice the debugger is, but I just can't see where the problem is. Do you know if you can download the final code sample somewhere?
Thanks for any help here. It's July 4th and I won't be sleeping for quite a while... :}
I have checked the document. You did something wrong. In the example the doneButton is a UIBarButtonItem.
Did you also do this:
To create the unwind segue, link the Cancel and Done buttons to the unwindToList: action through the Exit icon in the dock of the source view controller, XYZAddToDoItemViewController.
I am working on a split view controller app, i have two views, a detail view and a second view. When a value on my first view is selected. (say we select dog). my second view loads a few options on the left pane. Therefore from that left pane when i select Color. I want my uilabel to read."Dog is Black" or if I had selected cat from the first view, i want it to read Cat is yellow. when i select color.
So what i did is, in the didSelectRowAtIndexPath i have
if ([[animalArray objectAtIndex:indexPath.row]isEqualToString:#"dog"] )
{
secondView *detailList=[[secondView alloc]init];
[self.detailViewController.navigationController pushViewController:detailList animated:YES];
self.activeOption=#"dog";
//defined as #property (nonatomic, assign) NSString *activeOption; in .h file
}
Then in my secondView i have ( tried to extract the most important things
.h file
#property (nonatomic,retain) DetailViewController *detailViewController;
.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
if ([self. detailViewController.activeOption isEqualToString:#"dog"]) {
self.labelOne.text=#"The Dog is Black";
}
}
Now my problem is, when i click the row to load the second view (dog) i get this error.-[secondView activeOption]: unrecognized selector sent to instance .
Now i believe you get that when you call a method that doesn't exist, so while the method does exist, it doesn't exist in secondView but in the DetailView So i am at a lost as too why it is looking there for the method. even if i said self.detailviewController.
Thanks
I have an object that is sent from my main VC to my MasterTabViewController(UITabBarController) in the viewDidLoad I NSLog the object and it shows the object, good. Now I need that object to go to the first tab UIViewController. I tried multiple times and cannot get it to go. I am new so forgive my ignorance,
I send the object from my main vc to my MasterTabViewController via segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showLogin"])
{
MasterTabViewController *preview = segue.destinationViewController;
preview.communityTapped = self.tempCommunity;
}
}
^This works fine!^ self.tempCommunity is an instance community object.
MasterTabViewController.h
- (void)viewDidLoad
{
[super viewDidLoad];
FirstTabViewController *firstvc;
firstvc.communityTapped = self.communityTapped;
NSLog(#"%# !!!!!!!!! ",self.communityTapped.commDescription);
// Do any additional setup after loading the view.
}
FirstTabViewController.h
#property (nonatomic, strong) IBOutlet UILabel *descriptionLabel;
#property (nonatomic, strong) Community *communityTapped;
FirstTabViewController.m
- (void)viewDidLoad
{
self.descriptionLabel.text = self.communityTapped.commDescription;
[super viewDidLoad];
// Do any additional setup after loading the view.
}
If anyone could help that would be greatly appreciated as I have tried and failed many times.
You can't set up IBOutlets to a tab bar controllers view controllers (and I see from your project that you never hooked them up). In your viewDidLoad for the tab bar controller, you can get a reference to any of its view controllers with the viewControllers property. So do something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.firstvc = self.viewControllers[0];
self.firstvc.communityTapped = self.communityTapped;
NSLog(#"%# !!!!!!!!! ",self.communityTapped.commDescription);
}
I think the problem is in the viewDidLoad of your MasterTabViewController.h. You're creating the variable firstvc, but you're not setting the value to anything. So when you set the value of communityTapped in the next line, you're trying to set the value of communityTapped on nothing.
Option 1
If you've set up the tab in interface builder, you need to create an IBOutlet in your MasterTabViewController.h and connect it to your view controller. Something like the following:
#property (strong, nonatomic) IBOutlet FirstTabViewController *firstvc;
Then to set the value of the communityTapped, property, you would use something like this:
self.firstvc.communityTapped = self.communityTapped
Option 2
The other option would be to create the tab pragmatically, and add it to the view. I'm not sure quite what your setup is, but I imagine it would be something like this:
FirstTabViewController *firstvc = [[FirstTabViewController alloc] init];
firstvc.communityTapped = self.communityTapped;
NSArray *viewControllers = [[NSArray alloc] initWithObjects:firstvc, nil];
[self.navigationController.tabBarController setViewControllers:viewControllers animated:NO];
Using an instance of a UIViewController, is there any way I can find the UIPopoverController being used to present it? I would also want to find the UIViewController that displayed the UIPopoverController in the first place.
I would normally use a delegate or other sort of notification to send a signal from the displayed view controller to the displaying one, but in this case I'm trying to create a reusable custom segue that dismisses the popover and then moves on to another view in the main view.
You would think that this would be simple (the UIViewController even has a private _popoverController property!), but it is not.
The general answer is that you have to save a reference to the UIPopoverController in the UIViewController that it is presenting, at the time the UIViewController is created.
If you are creating the UIPopoverController programmatically, then that's the time to store the reference in your UIViewController subclass.
If you are using Storyboards and Segues, you can get the UIPopoverController out of the segue in the prepareForSegue method:
UIPopoverController* popover = [(UIStoryboardPopoverSegue*)segue popoverController];
Of course, be sure that your segue really is a UIStoryboardPopoverSegue!
My recommendation is to leverage a combination of your own custom property and the private APIs in UIKit. To avoid app store rejection, any private APIs should compile out for release builds, and should be used only to check against your implementation.
First let's build the custom property into a category on UIViewController. This allows some perks in the implementation, and it doesn't require you to go back and derive every class from some custom view controller subclass.
// UIViewController+isPresentedInPopover.h
#import <UIKit/UIKit.h>
#interface UIViewController (isPresentedInPopover)
#property (assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
#end
Now for the implementation - we'll be using the Objective C runtime's associated object API to provide the storage for this property. Note that a selector is a nice choice for the unique key used to store the object, as it's automatically uniqued by the compiler and highly unlikely to be used by any other client for this purpose.
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
#implementation UIViewController (isPresentedInPopover)
- (void)setPresentedInPopover:(BOOL)presentedInPopover
{
objc_setAssociatedObject(self,
#selector(isPresentedInPopover),
[NSNumber numberWithBool:presentedInPopover],
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isPresentedInPopover
{
NSNumber *wrappedBool = objc_getAssociatedObject(self, #selector(isPresentedInPopover));
BOOL userValue = [wrappedBool boolValue];
return userValue ?: [[self parentViewController] isPresentedInPopover];
}
#end
So there's a convenient side effect of using this as a category - you can call up to the parentViewController and see if that is contained in a popover as well. This way you can set the property on, say, a UINavigationController and all of its child view controllers will respond correctly to isPresentedInPopover. To accomplish this with subclasses, you'd be either trying to set this on every new child view controller, or subclassing navigation controllers, or other horrific things.
More Runtime Magic
There is still more that the Objective C Runtime has to offer for this particular problem, and we can use them to jump into Apple's private implementation details and check your own app against it. For release builds, this extra code will compile out, so no need to worry about the all-seeing eye of Sauron Apple when submitting to the store.
You can see from UIViewController.h that there is an ivar defined as UIPopoverController* _popoverController with #package scope. Luckily this is only enforced by the compiler. Nothing is sacred as far as the runtime is concerned, and it's pretty easy to access that ivar from anywhere. We'll add a debug-only runtime check on each access of the property to make sure we're consistent.
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
#implementation UIViewController (isPresentedInPopover)
- (void)setPresentedInPopover:(BOOL)presentedInPopover
{
objc_setAssociatedObject(self,
#selector(isPresentedInPopover),
[NSNumber numberWithBool:presentedInPopover],
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isPresentedInPopover
{
NSNumber *wrappedBool = objc_getAssociatedObject(self, #selector(isPresentedInPopover));
BOOL userValue = [wrappedBool boolValue];
#if DEBUG
Ivar privatePopoverIvar = class_getInstanceVariable([UIViewController class], "_popoverController");
UIPopoverController *popover = object_getIvar(self, privatePopoverIvar);
BOOL privateAPIValue = popover != nil;
if (userValue != privateAPIValue) {
[NSException raise:NSInternalInconsistencyException format:
#"-[%# %#] "
"returning %# "
"while private UIViewController API suggests %#. "
"Did you forget to set 'presentedInPopover'?",
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
userValue ? #"YES" : #"NO",
privateAPIValue ? #"YES" : #"NO"];
}
#endif
return userValue ?: [[self parentViewController] isPresentedInPopover];
}
#end
When using the property incorrectly, you'll get a message like this on the console:
2012-09-18 14:28:30.375 MyApp[41551:c07] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Consistency error in -[UINavigationController isPresentedInPopover]: returning NO while private UIViewController API suggests YES. Did you forget to set 'presentedInPopover'?'
...but when compiling with the DEBUG flag off or set to 0, it compiles down to the exact same code as before.
For The Free and the Foolhardy
Maybe you're doing Ad-Hoc/Enterprise/personal builds, or you're sufficiently bold to see just what Apple thinks about this one for the App Store. Either way, here's an implementation that just works using the current runtime and UIViewController - no setting properties needed!
// UIViewController+isPresentedInPopover.h
#import <UIKit/UIKit.h>
#interface UIViewController (isPresentedInPopover)
#property (readonly, assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
#end
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
#implementation UIViewController (isPresentedInPopover)
- (BOOL)isPresentedInPopover
{
Ivar privatePopoverIvar = class_getInstanceVariable([UIViewController class], "_popoverController");
UIPopoverController *popover = object_getIvar(self, privatePopoverIvar);
BOOL privateAPIValue = popover != nil;
return privateAPIValue ?: [[self parentViewController] isPresentedInPopover];
}
#end
Most helpful would probably be to make popover a class variable, so in the .m file of the class that is going to present the popover, do something like this:
#interface ExampleViewController()
#property (nonatomic, strong) UIPopoverController *popover
#end
#implementation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"some segue"])
{
//prevent stacking popovers
if ([self.popover isPopoverVisible])
{
[self.popover dismissPopoverAnimated:YES];
self.popover = nil;
}
[segue.destinationViewController setDelegate:self];
self.popover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
}
#end
As #joey wrote above, Apple eliminated the need for the dummy control in iOS 8 with the popoverPresentationController property defined for UIViewController as the "The nearest ancestor in the view controller hierarchy that is a popover presentation controller. (read-only)".
Here is an example in Swift for a UIPopoverPresentationController-based segue defined on a storyboard. In this case, a button has been added programmatically and can be defined in this way as the pop-over's anchor. The sender could also be a selected UITableViewCell or a view from it.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showCallout" {
let button = sender as UIButton
let calloutViewController = segue.destinationViewController as CalloutViewController
if let popover = calloutViewController.popoverPresentationController {
popover.sourceView = button
popover.sourceRect = button.bounds
}
}
}
Taking off from ndoc's anwser: this answer shows a neater way in iOS 6 to prevent a popover from showing multiple time through segues. The method in the link was the one that worked awesomely for me for preventing popover stacking.
If you JUST want to know if your controller is being presented inside a popover (not interested to get a reference to the popover controller), you can simply do this, without storing variables nor hacking private API's.
-(BOOL)isPresentedInPopover
{
for (UIView *superview = self.view.superview; superview != nil; superview = superview.superview)
{
if ([NSStringFromClass([superview class]) isEqualToString:#"_UIPopoverView"])
return YES;
}
return NO;
}