So I have my AppDelegate method trying to set an object in a ViewController method. My AppDelegate looks like this:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
//Grab a reference to the UISplitViewController
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
PatientDetailViewController* patientDetailViewController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = patientDetailViewController;
//Grab a reference to the masterviewcontroller and get the first patient in the list.
UINavigationController *patientNavController = [splitViewController.viewControllers objectAtIndex:0];
PatientMasterTableViewController *patientMasterViewController = (PatientMasterTableViewController *)[patientNavController topViewController];
Patient* firstPatient = [[patientMasterViewController patientArray] objectAtIndex:0];
//Set it as the detailviews patient.
[patientDetailViewController setPatient:firstPatient];
//Set the detail's as the left's delegate.
patientMasterViewController.delegate = patientDetailViewController;
}
return YES;
}
and the method to set the object looks like this:
-(void)setPatient:(Patient *)patient
{
if (![self.patient isEqual:patient])
{
self.patient = patient;
//Update the UI to reflect the new patient selected from the list.
[self refreshUI];
}
}
The issue that I'm having is that the setPatient method will be called non stop until the program crashes and I have no idea why. Can anyone shed some light on this?
This:
-(void)setPatient:(Patient *)patient
{
if (![self.patient isEqual:patient])
{
self.patient = patient;
//Update the UI to reflect the new patient selected from the list.
[self refreshUI];
}
}
Should be:
-(void)setPatient:(Patient *)patient
{
_patient = patient;
[self refreshUI];
}
Related
In my app I'm using a UISplitViewController. I wrote the code below to show a restaurant in the detail view.
- (void)tableView:(UITableView *)tableView didSelectRestaurant:(Restaurant *)restaurant {
UINavigationController *nvc = [[self.splitViewController viewControllers] objectAtIndex:1];
RestaurantsTabViewController *rtc = [[nvc viewControllers] objectAtIndex:0];
[rtc addTabWithRestaurant:restaurant];
}
This works fine on iPad, since it's rendering both the master and detail view. On iPhone it crashes on this line UINavigationController *nvc = [[self.splitViewController viewControllers] objectAtIndex:1]; though, because the detail view hasn't been rendered yet. How can I solve this?
Sorry for the Swift solution but I am pretty sure that you can adapt the code to your Objective-C environment. :)
Simply add a check to make sure that your rtc is not nil. If it is nil create a new instance and call the UISplitViewControllers showDetailViewController:sender: method:
func tableView(_ tableView: UITableView, didSelectRestaurant restaurant: Restaurant) {
if let detailNavigationController = splitViewController?.viewControllers.last as? UINavigationController,
let rtc = detailNavigationController.viewControllers.first as? RestaurantsTabViewController {
rtc.addTabWithRestaurant(restaurant)
} else {
let rtc = RestaurantsTabViewController()
rtc.addTabWithRestaurant(restaurant)
splitViewController?.showDetailViewController(rtc, sender: self)
}
}
UPDATE - Objective-C solution could look something like this:
- (void)tableView:(UITableView *)tableView didSelectRestaurant:(Restaurant *)restaurant {
if (self.splitViewController.viewControllers.count > 1) {
UINavigationController *nvc = [self.splitViewController.viewControllers objectAtIndex:1];
RestaurantsTabViewController *rtc = [nvc.viewControllers objectAtIndex:0];
[rtc addTabWithRestaurant:restaurant];
} else {
RestaurantsTabViewController *rtc = [RestaurantsTabViewController new];
[rtc addTabWithRestaurant:restaurant];
[self.splitViewController showDetailViewController:rtc sender:self];
}
}
In my app I have this hierarchy:
AppViewController (root)-->HUDViewController (as a container viewcontroller within AVC)-->NavBar (subview of UIView)-->UIButtons
When you touch some of the buttons they need to launch a UIPopoverController from AVC. I send a notification back from the NavBar class to AVC. In the selector for the notification center I have this code
...
//get the client list from the notification
[dictPopoverData setObject: [[notification userInfo] objectForKey:#"Client List"] forKey:#"Client List"];
//get the frame of the object launching the popover
popupCallerFrame = CGRectFromString([[notification userInfo] objectForKey:#"Caller Frame"]);
[self presentPopOver:CLIENT_LIST:YES:dictPopoverData];
...
then in presentPopOver I have this:
- (void) presentPopOver : (int) popoverID : (BOOL) isTable : (NSMutableDictionary*) dictPopoverData {
if (self.myPopoverController != nil) {
[self.myPopoverController dismissPopoverAnimated:YES];
self.myPopoverController = nil;
}
CGRect launchFrame;
//init the popover
if (popoverID == CLIENT_LIST) {
ClientListPopover* vcClientList = [[ClientListPopover alloc] initWithStyle:UITableViewStylePlain];
vcClientList.arrDataSource = [dictPopoverData objectForKey:#"Client List"];
self.myPopoverViewController = vcClientList;
//set the launch frame
launchFrame = popupCallerFrame;
launchFrame.origin.x = launchFrame.origin.x;
launchFrame.origin.y = launchFrame.origin.y + 100.0;
} else if (popoverID == PA_LIST) {
PAListPopover* vcPAList = [[PAListPopover alloc] initWithStyle:UITableViewStylePlain];
vcPAList.strClientNumber = [dictPopoverData objectForKey:#"Client Number"];
self.myPopoverViewController = vcPAList;
//set the launch frame
launchFrame = popupCallerFrame;
launchFrame.origin.x = launchFrame.origin.x;
launchFrame.origin.y = launchFrame.origin.y + 100.0;
}
//init and display the popover controller
if (self.myPopoverController == nil) {
//add the self.myPopoverViewController
self.myPopoverController = [[UIPopoverController alloc] initWithContentViewController:self.myPopoverViewController];
//display the popover
[self.myPopoverController presentPopoverFromRect:launchFrame inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
[self.myPopoverController setDelegate:self];
} else {
[self.myPopoverController dismissPopoverAnimated:YES];
self.myPopoverController = nil;
}
}
So what is suppose to happen is the user clicks the client button, notification gets sent to AVC, a UITable in a popover controller is presented. The user then selects a client from the table. Again, a notification is sent to AVC, the displayed client list popover should now be dismissed and the PA list popover should show. It seems that [self.myPopoverController dismissPopoverAnimated:YES] is not being called. I've traced it and it hits that line of code but nothing happens, the first popover remains on the screen. Any ideas of what I am doing wrong?
Edit: I forgot to mention I can't seem to assign a delegate to self.myPopoverController. Which is probably why the dismiss method is not firing.
Edit: I added the call to the delegate after the popover controller is inited. That didn't seem to make a difference. If I touch outside the first popover, without touching inside, it does dismiss and I can trace the method.
i am using two protocol something like this:
#protocol ModalClosedProtocol <NSObject>
-(void) modalClosedGlobalProtocolMethod;
#end
and syncmlClient Protocol.
There are three classes ContactsViewController, EventViewController, ImageSettingViewController.These classes use ModalclosedProtocol and syncmlClient protocol.all these three classes expects from SettingViewController class to implement ModalClosedProtocol and syncmlClient protocol.It is just a short overview of current implementation.
In my SettingViewController i am handling table delegate method didSelectRow.for code factoring i have created a separate method which is called from didSelectRow.. like this:
-(void) checkDeviceAndHandleModelSizeForFlip:(FlipsideViewController *)flipVc orContact:(ContactsViewController *)contactVc orEvent:(EventViewController *)eventVc orImage:(ImageSettingViewController *)imageSettingVc
{
UIViewController *genericVC;
if (flipVc!=nil)
{
genericVC = flipVc;
flipVc.modalClosedProtocolDelegate = self;
flipVc.syncmlClient = self.syncmlClient;
}
else if (contactVc!=nil)
{
genericVC = contactVc;
contactVc.modalClosedProtocolDelegate = self;
contactVc.syncmlClient = self.syncmlClient;
contactVc.mainViewController = self.mainViewController;
}
else if (eventVc!=nil)
{
genericVC = eventVc;
eventVc.modalClosedProtocolDelegate = self;
eventVc.syncmlClient = self.syncmlClient;
eventVc.mainViewController = self.mainViewController;
}
else if (imageSettingVc!=nil)
{
genericVC = imageSettingVc;
imageSettingVc.modalClosedProtocolDelegate = self;
imageSettingVc.syncmlClient = self.syncmlClient;
imageSettingVc.mainViewController = self.mainViewController;
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
genericVC.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:genericVC animated:YES completion:nil];
genericVC.view.superview.frame = CGRectInset(genericVC.view.superview.frame, 100, 50);
}
else
{
genericVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:genericVC animated:YES completion:nil]; //alok
}
}
basically if you see there is lot of redudancy in code in if/else clause.It would be better from code management point of view if i had a single UIViewController object.
genericVC = contactVc;//vc object passed from didSelectrow it may be contact/event/image
genericVC.modalClosedProtocolDelegate = self;
genericVC.syncmlClient = self.syncmlClient;
genericVC.mainViewController = self.mainViewController;
can some one help me regarding this problem.
Thanks
Just declare your genericVC like this:
UIViewController<ModalClosedProtocol, syncmlClient> *genericVC;
This way you let the compiler know that your genericVC View Controller conforms to the protocols ModalClosedProtocol and syncmlClient, so the methods you want to call are there.
[self checkDeviceAndHandleModelSizeForFlip:self.flipVc];//Pass contactVc, eventVc, imageSettingVc like that,
-(void) checkDeviceAndHandleModelSizeForFlip:(ViewController *)viewController
{
UIViewController *genericVC;
if (viewController!=nil)
{
genericVC = viewController;
viewController.modalClosedProtocolDelegate = self;
viewController.syncmlClient = self.syncmlClient;
viewController.mainViewController = self.mainViewController;
}
}
I have an app with a tab bar controller. One of these views is a table view. There is a method to set the badge of this view in the tab bar. This works...but only when the user touches this view and not right on launching the app. So I tried to use this method in appDelegate...but this doesn`t work.
my method in the view:
#property (strong) NSMutableArray *cars;
//some code here
-(void)SelectBadge
{
int r = [_cars count];
if (r == 0) {
self.navigationController.tabBarItem.badgeValue = 0;
}
else {
self.navigationController.tabBarItem.badgeValue = [NSString stringWithFormat:#"%d", r];
}
[self.tableView reloadData];
}
I tried to put this method in my appDelegate file:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
CarList *Instance = [[CarList alloc] init];
[Instance SelectBadge];
}
Thanks to all your answers beforehand.
The way I see it is you are creating a new intance of CarList in this - (void)applicationDidBecomeActive:(UIApplication *)application method. So In selectBadge function the self.navigationController.tabBarItem.badgeValue = someValue; will be setting badge value for some other instance.
Try addressing the correct instance. If you can access the UITabBarController instance then you can do this:
UITabBar *tabBar = mTabBarController.tabBar;
UITabBarItem *someItem = [tabBar.items objectAtIndex:0];////You can put your interested tabBarItem index
someItem. badgeValue = #"100";
Assuming your ViewControllers are loaded from a StoryBoard, call your function to update the tabBarItem badgeValue in 'initWithCoder:' of the ViewController whose tabBarItem badgeValue you want to update. The ViewControllers that are associated with tabs in the TabBarController are initialized when the TabBar loads.
The code could look something like this:
- (id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
NSString* badgeValue = [self calculateBadgeValue]; //your method
self.tabBarItem.badgeValue = badgeValue;
return self;
}
If you do this, the badge should update when the TabBar becomes visible.
Here's the property declaration in my SlidingVC header, a subclass of UITableViewController:
#property (strong, nonatomic) NSString *user;
Here's my synthesize line:
#synthesize user = _user,sortedWordlist = _sortedWordlist, wordlist = _wordlist;
Here's my custom init:
- (id)initWithUser:(NSString*)theUser
{
self = [super init];
if (self) {
if ([theUser isEqualToString:#"user"]) {
_user = #"user";
}
else if ([theUser isEqualToString:#"opponent"]){
_user = #"opponent";
}
}
return self;
}
So what's happening is that as I step pver initWithUser:, I see that _user takes on the memory address of theUser in the variable debugger window. I step over return self and then to the closing } of the method and it's still set. However, Xcode returns to return self one more time and then if I step over that _user doesn't have a memory address next to it anymore, and it remains null for the methods that follow.
Why is it returning twice and then setting to null the second time?
Here's the method in my MainVC that instantiates the SlidingVC:
- (WSWordlistTableViewController *)setupWordlistTableViewController:(NSString*)user
{
WSWordlistTableViewController *wordlistTableViewController;
UIView *wordlistContainerView;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle: nil];
if ([user isEqualToString:#"user"]){
if(!self.userWordlistTableViewController){
self.userWordlistTableViewController = [[WSWordlistTableViewController alloc] initWithUser:#"user"];
wordlistTableViewController = self.userWordlistTableViewController;
wordlistContainerView = self.userWordlistContainerView;
}
}
else if ([user isEqualToString:#"opponent"]) {
if(!self.opponentWordlistTableViewController){
self.opponentWordlistTableViewController = [[WSWordlistTableViewController alloc] initWithUser:#"opponent"];
wordlistTableViewController = self.opponentWordlistTableViewController;
wordlistContainerView = self.opponentWordlistContainerView;
}
}
wordlistTableViewController = [mainStoryboard instantiateViewControllerWithIdentifier:#"wordlistTableViewController"];
wordlistTableViewController.view.frame = wordlistContainerView.bounds;
wordlistTableViewController.view.autoresizingMask = wordlistContainerView.autoresizingMask;
[self addChildViewController:wordlistTableViewController];
[wordlistContainerView addSubview:wordlistTableViewController.view];
[wordlistContainerView bringSubviewToFront:wordlistTableViewController.wordlistTableView];
[wordlistTableViewController didMoveToParentViewController:self];
return wordlistTableViewController;
}
And the method that calls that, depending on which button is pressed:
- (IBAction)slideUserWordlistView:(id)sender {
if(!self.userWordlistTableViewController){
[self setupWordlistTableViewController:#"user"];
}
// (sliding drawer code here)
}
Or:
- (IBAction)slideOpponentWordlistView:(id)sender {
if(!self.opponentWordlistTableViewController){
[self setupWordlistTableViewController:#"opponent"];
}
// (sliding drawer code here)
}
What I'm doing is sliding out a view that contains my SlidingVC. I have two of them, one for each of two users. When each respective button is pressed I check if each respective SlidingVC exists, if not, instantiate then add to the the slidingViewContainer.