isKindOfClass Bool if statement logging NO - ios

I'm checking if toViewController, which is the 2nd Tab in my tabBar, is of the class MatchCenterViewController, but the else statement is running instead, which tells me that it's not of that class.
I'm positive that the UIViewController in that tab is connected to MatchCenterViewController, so what else could cause this if statement to not work?
NSLog(#"numberOfMatches is 1");
UIViewController *toViewController = [self.tabBarController viewControllers][1];
NSLog(#"toViewController: %#", toViewController);
if ([toViewController isKindOfClass:[MatchCenterViewController class]]) {
NSLog(#"2nd matchcenter if statement works");
MatchCenterViewController *matchViewController = (MatchCenterViewController *)toViewController;
matchViewController.didAddNewItem = YES;
NSLog(#"alright they're set, time to switch");
}
else {
NSLog(#"toViewController is not MatchCenterViewController");
}
[self.tabBarController setSelectedIndex:1];

You can add NSLog(#"toViewController is of class: %#", NSStringFromClass([toViewController class]); and see the actual view controller class.
Or if didAddNewItem is a property that only MatchCenterViewController has, you can try this way:
if ([toViewController respondsToSelector:#selector(setDidAddNewItem:)]) {
// this is MatchCenterViewController
} else {
// this is not MatchCenterViewController
}

Related

Change if statement with UISwitch

Hi I'm currently developing an app where you have a viewController with a UISwitch that i have got working with NSLog. My question is if you can change stuff in other viewControllers by creating a BOOL and then referring to that in the if statement? Heres some code:
UISwitch *mySwitch = [[UISwitch alloc] initWithFrame:CGRectMake(251, 111, 0, 0)];
[mySwitch addTarget:self action:#selector(changeSwitch:) forControlEvents:UIControlEventValueChanged];
[mySwitch setOnTintColor:UIColorFromRGB(0xac1f2d)];
[self.view addSubview:mySwitch];
- (void)changeSwitch:(id)sender{
if([sender isOn]){
NSLog(#"Switch is ON");
} else{
NSLog(#"Switch is OFF");
}
If you want to change something in another ViewController, you can do this by creating a reference to the other ViewController.
If you add public properties to the 'other' ViewController, you can change them in your if statement.
Say you have another VC, OtherViewController.
Create a BOOL property in it like:
#property(nonatomic) BOOL flag;
Implement the setter for the property in .m file of OtherViewController:
-(void)setFlag:(BOOL)flag {
_flag = flag;
if(_flag) { //if flag is true do something
//...........
}
else { //do anything else
//........
}
}
Now create a reference of OtherViewController instance in your first view controller, containing the switch. Say its referred by a property named other from the current view controller. Then you can trigger the required action on OtherViewController based on the switch's value from your present view controller like this:
- (void)changeSwitch:(UISwitch*)sender{
self.other.flag = sender.on;
}
This will call the setter of flag property in OtherViewController and would according to what you have written there.
Of course, the instance of OtherViewController should not be deallocated in between.
To expand on Stefan answer:
you would need to add property to the ViewController you want to change
so in ViewControllerA you would import the ViewControllerB header:
#import "ViewControllerB.h"
So that you can change that property in the method
- (void)changeSwitch:(id)sender{
ViewControllerB *viewcontroller = [[ViewControllerB alloc]init]];
if([sender isOn]){
viewcontroller.log = #"Switch is ON";
} else{
viewcontroller.log = #"Switch is OFF";
}
}
I'm interpreting your question to be asking whether you can change the state of another view controller using a switch on your existing view controller. The answer is yes.
You have a conditional to decide the state of the switch already. To make the connection you want to add a property to the view controller you are about to push. When you instantiate the new view controller, set the state of its property after checking if([mySwitch isOn]) (create an IBOutlet so you can check it at any time rather than responding to an IBAction), then push it. If you're going to get there via segue, you would check the state of the switch in prepareForSegue: instead.
Here's how you set the property in prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"uniqueSegueIdentifierFromIB"])
{
// Get a reference to the DestinationViewController
DestinationViewController *destinationVC = [segue destinationViewController];
// DestinationViewController has public #property (nonatomic) BOOL configurationOption;
if([mySwitch isOn])
{
[destinationVC setConfigurationOption:YES];
}
else
{
[destinationVC setConfigurationOption:NO];
}
}
}
Check the state of the new view controller's property in viewDidLoad: to decide how you want to configure it.

Check if a Specific Instance of class already exists

is there anyway to check if a specific instance of a class has already been created. I feel like it is hard to check if the instance already exists when there is a chance you may not have created it yet.
Here is my code:
-(IBAction)done:(id)sender
{ //I want to figure out how to check if 'newWindow' already exists before I create another 'newWindow'
SimpleTableView *newWindow = [self.storyboard instantiateViewControllerWithIdentifier:#"SimpleTableView"];
[self.navigationController pushViewController:newWindow animated:YES];
}
Thanks for all the help guys.
Yes, there is a simple way to do it.
You just need to have some reference to it (for example create a property) and check whether it is nil (not initialized) or not. You can do it like this:
if(!myReference){
//if you get here it means that it hasn't been initialized yet so you have to do it
}
First make newWindow an ivar or a property.
Then:
if (!newWindow){
newWindow = [self.storyboard instantiateViewControllerWithIdentifier:#"SimpleTableView"];
}
I wrote you a method that checks all viewControllers in UINavigationController:
- (BOOL)classExistsInNavigationController:(Class)class
{
for (UIViewController *controller in self.navigationController.viewControllers)
{
if ([controller isKindOfClass:class])
{
return YES;
}
}
return NO;
}
Use it like this:
- (IBAction)done:(id)sender
{
//I want to figure out how to check if 'newWindow' already exists before I create another newWindow
if (![self classExistsInNavigationController:[SimpleTableView class]])
{
SimpleTableView *newWindow = [self.storyboard instantiateViewControllerWithIdentifier:#"SimpleTableView"];
[self.navigationController pushViewController:newWindow animated:YES];
}
}
You can also do something like this:
- (UIViewController *)classExistsInNavigationController:(Class)class
{
for (UIViewController *controller in self.navigationController.viewControllers)
{
if ([controller isKindOfClass:class])
{
return controller;
}
}
return nil;
}
And use it like this if you want to pop to the viewController that exists already:
- (IBAction)done:(id)sender
{
//I want to figure out how to check if 'newWindow' already exists before I create another newWindow
UIViewController *controller = [self classExistsInNavigationController:[SimpleTableView class]];
if (!controller)
{
SimpleTableView *newWindow = [self.storyboard instantiateViewControllerWithIdentifier:#"SimpleTableView"];
[self.navigationController pushViewController:newWindow animated:YES];
}
else
{
[self.navigationController popToViewController:controller animated:YES];
}
}
You can use if/else to check newWindow exists or not.
if (newWindow) { // newWindow is exist to do something
// Do something.
} else { // newWindow is not exist to do something
// Do something.
}
You can implement an instance counter (https://stackoverflow.com/a/30509753/4647396) in the class you want to track.
Then just check if the counter is greater than 0.
If I interpret your question correctly you just want to know wether an instance exists and dont need a reference to it.

Dealloc is called on UIViewControllers which are stored in a NSMutableDictionary and presented by UIViewControllerContainment

I've built a custom UITabBarController with Storyboards/Segues and UIViewController containment. Here is a link to it: https://github.com/mhaddl/MHCustomTabBarController
The UIViewControllers which will be presented by the Container are stored in a NSMutableDictionary (keys are the segues' identifiers). Everything is working fine until the point is reached where I come back to a earlier presented ViewController. At this moment "dealloc" gets called on this ViewController before it is presented.
How can I prevent "dealloc" from getting called so it can be used to unsubscribe from Notifications, and nil delegates.
MHCustomTabBarController:
#implementation MHCustomTabBarController {
NSMutableDictionary *_viewControllersByIdentifier;
}
- (void)viewDidLoad {
[super viewDidLoad];
_viewControllersByIdentifier = [NSMutableDictionary dictionary];
}
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
self.destinationViewController.view.frame = self.container.bounds;
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![_viewControllersByIdentifier objectForKey:segue.identifier]) {
[_viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
for (UIButton *aButton in self.buttons) {
[aButton setSelected:NO];
}
UIButton *button = (UIButton *)sender;
[button setSelected:YES];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [_viewControllersByIdentifier objectForKey:self.destinationIdentifier];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([self.destinationIdentifier isEqual:identifier]) {
//Dont perform segue, if visible ViewController is already the destination ViewController
return NO;
}
return YES;
}
#pragma mark - Memory Warning
- (void)didReceiveMemoryWarning {
[[_viewControllersByIdentifier allKeys] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
if (![self.destinationIdentifier isEqualToString:key]) {
[_viewControllersByIdentifier removeObjectForKey:key];
}
}];
}
#end
MHTabBarSegue:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
"At this moment "dealloc" gets called on this ViewController before it is presented." -- no, not really. Dealloc is being called on a controller that never gets on screen, not the one you came from initially or are going back to. The way your segue is set up, and the fact that you keep a reference to your controllers in the dictionary, means that they never get deallocated. Segues (other than unwinds) ALWAYS instantiate new view controllers, so what's happening is that a new instance of, say VC1 is created when you click on the first tab (and a segue is triggered), but you never do anything with that controller (which would be self.destinationViewController in the custom segue class) so it's deallocated as soon as the perform method exits.
Depending on where you setup any delegates or notification observers, this might not be a problem -- this controller that's created, and then immediately deallocated never has its viewDidLoad method called, so if you do those things in viewDidLoad, they won't ever happen for this transient view controller.
If you don't want this to happen, then you need to make your transitions in code without using segues.

Property getting set then reset in init

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.

Modalview information from another viewcontrol

I am trying to send label data from one viewcontrol to be presented in another view control.
The code sends the info from the button, what I need to understand is how to send the Label info.
Opens the modal:
-(IBAction) showModal:(id)sender {
outputLabel.text=#"Nothing Chosen";
[[(ContentViewController *)self.parentViewController outputLabel]
setText:[sender currentTitle]];
switch (transitionStyle.selectedSegmentIndex) {
modalContent.modalTransitionStyle=
UIModalTransitionStyleCrossDissolve;
break;
}
[self presentModalViewController:modalContent animated:YES];
}
Received the modal back:
-(IBAction) hideModal:(id)sender {
outputLabel2.text=#"12" ;
[[(Corina3ViewController *)self.parentViewController outputLabel]
setText:[sender currentTitle]];
[self dismissModalViewControllerAnimated:YES];
}
I want to send the label info to the model. The label is "clearbLabel".
Create a label object in your modalView and set it at the - showModal: method
modalView:
// #interface
UILabel *clearLabel
// don't forget to #synthesize
-(IBAction) showModal:(id)sender {
// your code;
[modalView setClearLabel: clearLabel];
}
You can even nil it out after:
-(IBAction) hideModal:(id)sender {
// your code;
[modalView setClearLabel: nil];
}

Resources