So, I have one ViewController that has a TableView populate with an Array (_vinhoArray) and another TableViewController that opens when a user taps a row in this first ViewController.
What I want is set the title of second View ( TableViewController) for the name of row selected.
In this first one I have this code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selectedRow = [_vinhoArray objectAtIndex:indexPath.row];
// SubTable is the name of Second View
SubTable *subTable = [[SubTable alloc] init];
subTable.titleView = selectedRow;
}
In the second view a have a property set
#property (nonatomic, strong) NSString *titleView;
In the -(void)viewDidLoad I have this code
self.title = titleView;
But nothing shows in the title of the Second View (TableViewController)
Thats it! Please Help
If you simply want to set the title on the navigation bar of the view controller you are presenting, there's no need to create a property for it. You probably want to do something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *titleString = [_vinhoArray objectAtIndex:indexPath.row];
// SubTable is the name of Second View
SubTable *subTable = [[SubTable alloc] init];
subTable.title = titleString;
[self.navigationController pushViewController:subTable animated:YES];
}
Above seems to be the easiest and most straight-forward way to implement this. On a side note, it's pretty unconventional to name a variable type like an NSString with some other class type, like titleView or titleLabel. Something similar to "titleString", like I used, would be more appropriate and less confusing for anyone reading your code.
What is probably happening is that the view is loading before you set the titleView property. As you are setting the title statically in viewDidLoad, no changes are being shown. You can confirm this by putting in a couple of breakpoints and seeing the order of the calls being made.
One solution to this is to use a more dynamic method of setting the table. Create a custom accessor in your second view controller:
- (void)setTitleView:(NSString *)title {
_titleView = title; // This sets the backing iVar
self.title = titleView;
}
Now, whenever you set the property, then the change is applied, even if it is after the view has loaded.
You don't need to declare the method or the iVar, these are set up by autosynthesis; you are merely overriding the default implementation of the property setter.
Are you certain that setting .title property is what you want? If you're in a UINavigationController you may actually want
self.navigationItem.title
instead. Setting the title is independent of source of the title, so I'd work on getting that second View Controller to display a static title you choose, and only then turn to the task of passing a title dynamically from the first VC. Your issue is likely in setting the title string, not in how you pass that string around from VC 1 to VC 2.
As obvious as it seems, the .title property of a UIViewController doesn't always actually cause a title string to be displayed… the rules for when that property is actually used get tricky.
I am assuming that "titleView" is nothing but of type NSString.
In your SecondViewController.h, synthesize your titleView property.
And NSLog it in your SecondViewController before assigning it to self.title just to check that it's getting some value we tried to assign.
Hope this helps!
Related
I have two View Controllers: SavePopOverVC and MainVC. I also have a nib file called SavePopOver. SavePopOver has three items, a UIButton, a UIImage and a UITextView. The image and text view have outlets to property fields in SavePopOverVC called captionImage and captionTextView respectively. The button has an outlet to an IBAction in SavePopOverVC.
In MainVC.m I have the following two lines in my class extension.
SavePopOverVC *spvc;
UIPopoverController *popover;
In my viewDidLoad of the same file I have the following lines relating to my popover.
spvc = [[SavePopOverVC alloc] initWithNibNamed:#"SavePopOver" bundle:nil];
popover = [[UIPopoverController alloc] initWithContentViewController:spvc];
In my function that displays my popover, also in MainVC.m, I have the following lines.
[popover setPopoverContentSize:CGSizeMake(600,200)];
[popover presentPopoverFromRect:_header.frame inView:self.view permittedArrowDirection:UIPopoverArrowDirectionAny animated:YES];
[((SavePopOverVC *)popover.contentViewController).captionTextView setText:#"Some text here"];
However, captionTextView is nil when I make the setText: call. The app doesn't crash but the text isn't set. After the popover is displayed and I click on the UIButton to save the string typed in captionTextView I get the string just fine. So, I know the two are ultimately linked correctly, but how can I set captionTextView from when I display the popover?
If it is worth noting, I'm developing solely for iPad with this one.
It is most likely nil because its view isn't loaded at the time you set the text. Unlike most other modern languages, in Objective-C calling a method on a nil object doesn't cause an exception, it just does nothing.
To solve this, you can create a custom NSString property in your SavePopOverVC, e.g.
#property (nonatomic, copy) NSString *caption;
Before you call presentPopoverFromRect:, assign a value to this property. Inside SavePopOverVC, override viewDidLoad and set the captionTextView.text = self.caption;
There might be people who disagree with me, but I don't recommend exposing UI controls as properties in a view controller. This behaviour is one of the reasons for that.
I have one UIScrollView & one UIButton in my page.when I click one my button I go to next page with this code:
SearchViewController *add = [[SearchViewController alloc]initWithNibName:#"SearchViewController" bundle:nil];
[self presentViewController:add animated:YES completion:nil];
in next page I have one TableView. Now I want change contentOffset my ScrollView when click on any cell.when call one method in my ScrollViewController this method working but my ScrollView in my method is nil and contentOffset not working!!! why?
please guide me about that.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[scrolObject ChangeToLastObject:1];
[self dismissViewControllerAnimated:YES completion:nil];
}
this method is in ScrollViewController
- (void)ChangeToLastObject:(NSString*)idstation{
myScrollView.contentOffset = CGPointMake(myScrollView.frame.size.width * (2), 0);
}
why myScrollView in top method is nil???
myScrollView is IBOutlet (nonatomic,strong)
There's a lot wrong with this line:
[scrolObject ChangeToLastObject:1];
The primary issue is that scrolObject appears to be intended as a pointer to an instance of your ScrollViewController. It is probably uninitialized. Even if it were correctly initialized, it's poor design to give view controllers pointers to each other.
So how to communicate between view controllers? There's a half dozen answers to that question, including this one: Don't do it at all. Instead have your view controllers write to and read from the same model.
Your view controller with the table can write to the model when a table selection is made, and your ScrollViewController can read the model and set it's scroll view's content offset accordingly on viewWillAppear:.
Other things wrong with the line: scrolObject should probably be spelled scrollObject, ChangeToLastObject: should definitely be spelled starting with lowercase C, you're passing a literal int = 1, but the method is declared to take an NSString *.
A rather basic question I'm unsure about. I typically set up my UIViewController's view-related code in viewDidLoad. If the controller has some properties for labels, etc, this is where I would initialize them and add them to the view. These properties are usually declared in the .m so can be considered pseudo-private.
However - if the controller exposes one of these properties (let's say a UILabel) in its header file, I am finding that I can't rely on it existing when it comes time to set it up. For example:
CustomViewController *controller = [CustomViewController alloc] initWithNibName:nil bundle:nil];
controller.someLabel.text = #"label text goes here";
//then comes the presentation code
I find that I am setting the label's text too early - viewDidLoad has not fired yet so the label is nil.
Should I create this label in init and add it in viewDidLoad? Should I be doing all my set up in init? Or maybe all the initialization of view properties? Or judge it on a case by case basis?
Or maybe the root cause is that I shouldn't have a controller exposing a view (the label) and use some other pattern?
I'm looking for a consistent way to structure my code.
Yeah, you are pretty much right already. The thing is, all views components of your controller are not loaded until the view is actually presented. So you cannot set anything of your IBOutlets from outside the controller.
One approach for passing, for example, a text for an UILabel, it's create a new string property, let's say self.myString, assign it from outside, and in your viewDidLoad, set in the labels' text this property.
CustomViewController *controller = [CustomViewController alloc] initWithNibName:nil bundle:nil];
controller.myString = #"label text goes here";
And inside the CustomViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
(...)
self.label.text = self.myString;
}
I tend to do something like the following, which works for me if I only want to update the view on demand (if I want to update it more frequently then I would do so in viewWillAppear or via KVO or some other notification mechanism).
Have some private method that does my UI setup based on the property:
- (void)_updateUIForProperty {
// Handle UI update
}
Implement a setter for my public property that calls the _updateUIForProperty method if the view has been loaded already:
- (void)setProperty:(<#property type#>)property {
_property = property;
if(self.isViewLoaded) {
[self _updateUIForProperty];
}
}
And then to handle the case where the property was set prior to the view loading, we do something like this in viewDidLoad:
- (void)viewDidLoad {
// Other initialization
if(_property != nil) {
[self _updateUIForProperty];
}
}
This may sound silly, but read on...
I want to set the text of a UILabel from outside of a UIViewController that is instantiated by a storyboard. I need to make sure that the label property of the view controller is set when I set its text otherwise the label's text won't be set(because it won't be loaded yet to receive a text value).
Here's my current solution:
// Show pin entry
if (!self.pinViewController) {
// Load pin view controller
self.pinViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"pinScreen"];
self.pinViewController.delegate = self;
if (!self.pinViewController.view) {
// Wait for pin screen to fully load
}
[self.pinViewController setMessageText:#"Set a pin for this device"];
}
Initially I had a while loop that looped until the value of view was not nil, But it seems the very act of checking the view loads it(as mentioned here: http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW37)
I tried using the isViewLoaded method with no success. It just looped forever.
I've gone forward with the above code as my current solution, but it feels wrong.
Is there a better way ensure a UIView has loaded?
I want to propose an alternative way where you don't have to rely on the availability of the view.
If you need to wait for the view to load before you can call other methods on your viewController you break encapsulation, because the viewController that calls your PinViewController has to know about the inner workings of your PinViewController. That's usually not a good idea.
But you could save objects like NSStrings in the PinViewController instance, and when the view of the PinViewController will appear you set its views according to the properties you have set before.
If you need to change the text of an label from outside your viewController you can also create a custom setter that sets the label.text for you.
Your .h
#interface PinViewController : UIViewController
#property (copy, nonatomic) NSString *messageText;
// ...
#end
And your .m
#implementation PinViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.messageLabel.text = self.messageText;
}
// optional, if you want to change the message text from another viewController:
- (void)setMessageText:(NSString *)messageText {
_messageText = messageText;
self.messageLabel.text = messageText;
}
// ...
#end
viewDidLoad should solve this I guess.
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html
I would rather see you change your logic and do it the way that #MatthiasBauch shows in his answer. However, to answer your actual question, you can simply set a view property in order to force it to load:
self.pinViewController.view.hidden = NO;
Using storyboard, I have a table view controller containing multiple dynamic prototype cells. One of the cells is a custom dynamic prototype cell (for which I created a subclass) containing a label and a switch. I have the action for the switch wired to an action method (say switchChanged:) in the view controller. In cellForRowAtIndexPath, I configure and return the cell appropriate for the specified index. So far so good.
The problem: my application has multiple instances of these custom cells, so how do I differentiate which switch has changed? One thought is that in the view controller I can create a member switch for each cell and link them to a specific cell switch when cellForRowAtIndexPath is called for the first time. Then I can use these member switches to compare with the switch that is passed into switchChanged:.
I know that I can try it and get an immediate answer, but I was hoping for a discussion of how other developers do what I am trying to do. Is this the best/worst/ok approach and what are practical alternatives?
Regards,
--John
I had this situation once (not with switches, but I believe it applies just the same). I've managed to get around it by subclassing the object class and adding the required properties/methods inside the subclass.
When calling the action, your sender will be the subclass, and you can access your added code there.
I don't know if it is the case, but if you're only trying to change a value, you should use bind the switch value to the property when creating the object. It will not even need an IBAction to call.
EDIT: Example:
#interface MySwitch : UISwitch
#property (nonatomic, assign) NSUInteger someProperty;
#end
Then, every time you create a cell, you can set "someProperty" to anything you want.
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
// yada yada yada...
UITableViewCell *cell;
// yada yada yada...
[cell.myLinkedSwitch setSomeProperty:indexPath.row];
return(cell);
}
Unless you're not creating your cells using the tableView:cellForRowAtIndexPath: method. Then you probably should use bindings to get your value to the right place.
Instead of adding a separate subclass, I just stored the row in each button Disabled Title property. This worked very will with little effort. This first code is in the CellForRowAtIndexPath:
NSString *strRow = [[NSString alloc] initWithFormat:#"%i",useRow];
[btnPreferredChk setTitle:strRow forState:UIControlStateDisabled];
Then my action method for the button uses that value to perform the appropriate activity.
- (IBAction)goStorePick:(id)sender
{
UIButton *useButton = [[UIButton alloc] init];
useButton = sender;
NSInteger *storeRow = [[useButton titleForState:UIControlStateDisabled] integerValue];
NSString *CMIMsg = [[NSString alloc] initWithFormat:#"goStorePick Method Executed at Row: %i", storeRow];
[self shwMessage:CMIMsg];
}
This worked well for me.