UILabel is always nil - ios

I have the following problem.
This is my Code:
GTSearchViewController* vc = [[GTSearchViewController alloc] initWithNibName:#"GTSearchViewController"
bundle:nil];
vc.headlineLabel.text = [[self.categorieArray objectAtIndex:indexPath.row] valueForKey:#"categoryName"];
[AppDelegate().centerViewController pushViewController:vc
animated:YES];
As you can see, I try to push a New ViewController with a xib File and want to set a String in my headlineLabel from my Array self.categorieArray. But when the SearchController gets pushed, everything works perfect, but my headlineLabel is always nil.
I don't understand why, I thought I donĀ“t need to call "alloc-init" in the SearchController because everything is build with Interface Builder?

Your problem is changing UI before it actually is loaded.
You should set label's text in ViewDidLoad of your view controller (it is called after the view is loaded).
Make NSString property in your SearchViewController class and set it right after initialization.
vc.headlineText = [[self.categorieArray objectAtIndex:indexPath.row] valueForKey:#"categoryName"];
Then in SearchViewController you should override viewDidLoad method:
- (void) viewDidLoad{
[super viewDidLoad];
self.headlineLabel.text = self.headlineText;
}
P.S. Of course I assume that you've set headlineLabel outlet correctly (from Interface Builder).

Related

Toggle edit mode in another ViewController

I have a ViewController (with a container view) embedded in a Navigation controller. The container contains a pageViewController with one of the 'pages' being a TableViewController (with UITableView outlet: 'aTableView'). I want to trigger the edit mode in the tableViewController when tapping a custom editButton in the navigation bar. When I create a custom editutton in the tableViewController the edit mode works as expected, but when I use the custom editButton in the navigation bar the setEditing bool value remains zero even when I setEditing to YES in the editButton selector. Here's the code:
ViewController.m
-(void)viewDidLoad {
self.editBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.editBtn setFrame:CGRectMake(0, 0, 40, 22)];
[self.editBtn addTarget:self action:#selector(goToToggleEdit:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *editButton=[[UIBarButtonItem alloc]initWithCustomView:self.editBtn];
self.navigationItem.rightBarButtonItem = editButton;
}
-(void)goToToggleEdit:(id)sender
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
TableViewController *tvc = [storyboard instantiateViewControllerWithIdentifier:#"aTableViewController"];
if(something==foo){
[tvc toggleEdit];
}
}
aTableViewController.h
#interface aTableViewController : UITableViewController <UITextFieldDelegate> {
IBOutlet UITableView *aTableView;
}
-(void) toggleEdit ;
#end
aTableViewController.m
-(void)toggleEdit {
[aTableView setEditing:YES animated:YES];
NSLog(aTableView.editing ? #"Yes" : #"No"); // --> logss 'No'.
if (aTableView.editing){
//do something
}
else {
//do something else
}
}
How can I efficiently trigger the edit mode in the tableViewController this way?
Edit
#Bagrat Kirakosian pointed out to me that my view hierarchy (Navigation Controller > View Controller (with containter) > Page View Controller (in container) > Table View Controller) might be the problem. I just want to create a Navigation Bar (with an edit button) that is fixed, therefore I can't embed the Table View Controller directly in a Navigation Controller.
Thanks.
UPDATE: Solution
I have accepted #sebastien's solution although both #sebastien's and #Bagrat's solution work great. #Bagrat's answer includes direct access to the Table View Controllers, while #sebastien's solution calls edit mode in the pageViewController. I think, considering the tricky hierarchy, the latter is a bit more secure.
Here is the code for my View Controller that totally work fine. be sure you configure your bar button in the right method of View Controller lifecycle. Also be sure that your #selector is properly implemented in your code.
In the same View Controller put these two blocks of code
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIBarButtonItem *rightBarButton = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:#"edit_icon.png" ] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStylePlain target:self action:#selector(edit:)];
[rightBarButton setTintColor:[UIColor whiteColor]];
self.navigationItem.rightBarButtonItem = rightBarButton;
}
Afterwards you need also to put your edit selector
-(void)edit:(UIButton *)sender {
// Toggle edit by inverting current edit state
// Also in this block change your right bar button text or image
[self.tableView setEditing:!self.tableView.editing animated:YES];
}
UPDATE 1
After your comment we got to whole another question. You problem is not in the part where you try to call toggle edit. Your problem is the wrong hierarchy of controllers (Navigation Controller > View Controller > Page View Controller > Table View Controller). This might cause a problem. Try to change your controllers like this;
UINavigationController > UIPageViewController > UIViewController(s)
Also it's a good practice to have a UITableView in UIViewController rather than using really dead UITableViewController. Don't forget to connect your tableView IBOutlet (by the way you didn't need it in UITableViewController), also connect datasource and delegate to Files owner. In your MyTableViewVC.h file add this line
#interface MyTableViewVC : UIViewController <UITableViewDataSource, UITableViewDelegate>
After that all your calls will work fine.
UPDATE 2
After analyzing your entire structure I found a mistake that you do every time on the button click.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
tvc = [[TodolistTableViewController alloc] init];
When you call storyboard every time it's ok but when you do [[TodolistTableViewController alloc] init] you are RE-MAKING the same table view controller every time but not even adding it to your main view. As I told you, your tricky hierarchy might cause difficulties but it has a solution.
In your PageViewController.m make tv1 and tv2 properties in .h file, like so.
#property (strong, nonatomic) UITableViewController *tv1;
#property (strong, nonatomic) UITableViewController *tv2;
Then in the view controller file do this
-(void)toggleEdit:(id)sender
{
PageViewController *current = (PageViewController *)[[self childViewControllers] firstObject];
if ([current isKindOfClass:[PageViewController class]])
{
[((TodolistTableViewController *)[current tv1]) toggleEdit];
}
}
Answer includes all security checks and direct access to your table view controllers, because you might need to change other properties/call functions later.
Now in -(void)toggleEdit:(id)sender you don't re-create your views every time but you catch the ones you already have in your current View Controller.
Good Luck!
Ok, your issue here is that you are trying to access an embedded controller in a wrong way.
You are actually managing 2 differents PageViewController:
The one you generated through your storyboard
The other one you are initiating in your code further
That's why you can't reach the expected result.
First of all, add a new method to your PageViewController:
PageViewController.h:
- (void)editTableAtIndex:(int)index;
PageViewController.m:
- (void)editTableAtIndex:(int)index {
[[self viewControllerAtIndex:index] setEditing:YES];
}
Now, in your main ViewController, access the PageViewController by using childViewControllers:
-(void)toggleEdit:(id)sender
{
PageViewController *pvc = self.childViewControllers[0];
[pvc editTableAtIndex:0];
}
It should be editing your TodoListTableView:
(Please notice that I used [pvc editTableAtIndex:0];, instead you should be calling something like [pvc editTableAtIndex:_PageViewController_current_index_];)

Delegation: delegated method doesn't see outlets

I have two views in the Tab Bar View. I want my second view to inform first view, when its Text Fields have changed value. I've done all the necessary coding for it, but there is one problem - first view doesn't see connected label outlets in the method declaration.
Code of the second view:
- (IBAction)textFieldHasChanged:(UITextField *)sender {
id<HPAAddCarOverallInfoTVCDelegate> strongDelegate = [[HPAAddCarMainViewController alloc] init];
if([strongDelegate respondsToSelector:#selector(addCarOverallInfoVC:textFieldValueChanged:)]) {
[strongDelegate addCarOverallInfoVC:self textFieldValueChanged:sender.text];
}
}
Code of the first view:
-(void)addCarOverallInfoVC:(HPAAddCarOverallInfoTableViewController *)viewController textFieldValueChanged:(NSString *)value
{
self.overallVCFieldCount.text = value;
NSLog(#"%#", value);
}
self.overallVCFieldCount.text = value; - value exist, but textField doesn't.
As I think, problem belongs at this line of code:
id<HPAAddCarOverallInfoTVCDelegate> strongDelegate = [[HPAAddCarMainViewController alloc] init];
I guess, that delegate isn't exact view with which I am working with. Bouth views are loaded at the same time via storyboard. If I am correct with my thought, can you tell me please, how can I give a pointer to exact first view which as second view are loaded when Tab Bar View controller goes on the screen?
You're creating a new view controller in textFieldHasChanged. If you have that view controller in IB, instantiate it like this:
UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
id<HPAAddCarOverallInfoTVCDelegate> strongDelegate = st instantiateViewControllerWithIdentifier:#"identifier"];
Where identifier is the identifier you have given your view controller in your storyboard.

Push View Controller, Black Screen

I'm pushing to a new view controller and passing some data to it. When I run the application I can press the button and push to a new view but the screen is completely black. Any help is appreciated.
- (IBAction)button:(id)sender {
NSString *firstField = self.field.text;
NSString *secondField = self.field2.text;
self.resultsArray = [[NSArray alloc] initWithObjects:firstField, secondField, nil];
NSUInteger randomResult = arc4random_uniform(self.resultsArray.count);
self.label.text = [self.resultsArray objectAtIndex:randomResult];
ImagesViewController *ivc = [[ImagesViewController alloc] init];
ivc.label = self.label.text;
[self.navigationController pushViewController:ivc animated:YES];
}
When you're using a storyboard, and you want to push a view controller in code (rather than with a segue), you need to give the controller an identifier, and create it like this:
ImagesViewController *ivc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyIdentifier"];
ivc.label = self.label.text;
[self.navigationController pushViewController:ivc animated:YES];
The view controller you are pushing is not having any frame dimension set.It is always recommended to call designated init for objects. For view controllers, designated init method is
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
if you have a xib assign it like
ImagesViewController *ivc = [[ImagesViewController alloc] initWithNibName:<your xib> bundle:[NSBundle mainbundle];
if you are using custom view, assign a frame dimension and add it as subview
Xcode 7.3 and Swift 2.2
In my case, I had made changes in the storyboard and made it a TabBarController and accordingly changed the class of the controller from UIViewController to UITabBarController. After some tweaking, this change wasn't favourable and I un did all the changes and got a black screen then because I had forgotten to change the class of the controller. I changed it back to UIViewController and it started working again.
So check if you have made the same mistake. The black screen came because the storyboard had a class(UIView/UITabBar/UITableView Controller) but that wasnt the same in code.
This can also happen if you have somehow got an incorrect connection between one of the subviews in the storyboard to the controller's view. Check the Referencing Outlets are correct in each of your subviews.
I got a good one:
Make sure you are implementing the right Super class, delegate, etc.. in the top part of the viewController you are trying to present. i.e.
I wasn't using/implementing UINavigationController at all
class TMDetailBlogViewController: UINavigationController {
//code goes here
}
After
class TMDetailBlogViewController: UIViewController {
//code goes here
}
Typically, you transition to another view controller by calling:
initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
on your custom UIViewController.
If you're not using a xib file, then what you're doing may be fine. Are you dynamically creating your UI elements within the constructor of your ImagesViewController?
I was trying without using storyboard, and its just that the default screen it uses is in black color. I changed the background color to white and it worked.
Pushed the controller this way-
NextController *nextController = [[NextController alloc]init];
[self.navigationController pushViewController:nextController animated:YES];
In NextController-
(void)viewDidLoad{
[self.view setBackgroundColor:[UIColor whiteColor]];
}

instantiateViewControllerWithIdentifier in Storyboard

I've made a tabbar controller with two views (standard setup from the template). First view is accessing an array within an dataController object. When I tap to the second view on the tabbar, I want the dataController reference to be set on the second viewController's dataController property.
I use call
SecondViewController *vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"SecondViewController"];
vc.dataController = self.dataController;
However, when I call my countArrayObjects on the secondViewController's dataController property, I always get 0 back - eventhough there should be objects in the array and I can't figure out why?
I've set the identifier on the secondViewController in MainStoryBoard and I have no crashes, the property just isn't set (I think).
For testing purposes I call the instantiateViewControllerWithIdentifier from a simple button, before tapping on to the secondView. And I call the countArrayObjects from viewDidLoad on secondViewController.
Any ideas?
Hmmm... I was having the same problem. At the moment, the only way I got around this is to test for the views superview being nil - which as its accessing the view selector of the class fires up the view did load event...
both of us are doing something wrong here though...
This is the test code I wrote...
SummaryViewController *sc = [self.childViewControllers objectAtIndex:0];
if(sc.view.superview == nil) sc.checkInTime.alpha = 0;
//-- by inspecting the value, it will call the viewDidLoad method of the controller.
It works, but it's wrong.

Unit testing (Kiwi): Property is always nil in test after loadView and viewDidLoad are called

I am writing a unit test template project for my own reference's sake. This includes template code for all the basic tasks, such as verifying that a UILabel's text property is set as expected after viewDidLoad.
I have a storyboard with a ViewController with one UILabel. In viewDidLoad I set it's text. The test then asserts whether or not the text has been set as expected, but for some reason, the UILabel is always nil. Everything works as expected (thank god) when I run the app; a white view is shown with a label with the expected text.
I am using Kiwi as my test framework and Xcode v4.5.
Here's some code:
ViewController.h:
#property (strong, nonatomic) IBOutlet UILabel *myLabel;
ViewController.m:
- (void)viewDidLoad {
myLabel.text = #"test";
}
ViewControllerTests.m:
ViewController *vc = [[ViewController alloc] init];
// create view in memory by calling view's getter
[vc view];
// set myLabel.text to "test"
[vc viewDidLoad];
[vc.myLabel shouldNotBeNil];
[[vc.myLabel.text should] equal:#"test"];
The above test fails, telling me that myLabel is nil.
This StackOverflow answer states that calling loadView will create the view hierarchy in memory and thereby prepare any subviews for use. According to the documentation, simply calling a ViewController view's getter will call loadView for you and create all views. Therefore, I find it extremely odd that myLabel = nil at the time of testing.
In your view controller, you are not assigning a value to self.myLabel.
self.myLabel = [[UILabel alloc] init];
This means self.myLabel has a nil value, so the following sequence occurs: [self myLabel] returns nil, [nil setText:#"test"] does nothing. All of this is standard behavior.
When you load the view controller from the nib or storyboard, the label is assigned according to how the outlets are drawn in IB, but calling ViewController *vc = [[ViewController alloc] init] does not loading the nib or storyboard.
Update
After re-reading my post, I think I left the wrong impression.
You need to load from a nib or storyboard, not add self.myLabel = [[UILabel alloc] init].
To load from a nib, use -initWithNibName:bundle: instead of -init. To load from a storyboard, first load the storyboard use +[UIStoryboard storyboardWithName:bundle:], then load the view controller from the storyboard using -[UIStoryboard instantiateViewControllerWithIdentifier:].

Resources