I have two UIView contained in a UIViewController - firstView and secondView - that I initialize pragmatically. I have a UILabel in the firstView, and a UIButton in the secondView. I would like the button in the second view to change the label in the first view. In the implementation file of the second view I have the following code:
- (void) changeLabel: (UIButton *) sender{
firstView *view = [[firstView alloc] init];
view.label.text = #"Changed Text";
}
However I figured out that the above method just initializes a new class of firstView and does not link to the existing UIView. How can I change properties of firstView from within secondView?
Create properties in your view controller's header file for the views:
#property (nonatomic, retain) UIView *firstView;
#property (nonatomic, retain) UILabel *label;
When you create the view and label assign them to the properties:
self.firstView = // create your view here
self.label = // create your label here
Create a button property on your UIView object so you can access it later:
#property (nonatomic, retain) UIButton *button;
Then in your view controller file, when you create everything, access the view's button property and add a target, like this:
[firstView.button addTarget:self action:#selector(changeLabel) forControlEvents:UIControlEventTouchUpInside];
Then you can simply have the method your button calls be like this:
- (void)changeLabel {
self.label.text = #"Changed Text.";
}
Related
View does not know about the model. I have custom view:
#interface CustomView : UIView
#property UIImageView *imageView;
#property UILabel *labelView;
#property UILabel *dateLabelView;
#property UIImageView *badgeView;
...
#end
Set data in ViewController:
-(void)viewDidLoad
{
[super viewDidLoad];
...
//Install data from model in view
self.customView.labelView.text = self.model.title;
self.customView.badgeView.hidden = self.model.isBadge;
self.customView.dateLabelView.text = [self formatDate:self.model.createDate];
...
}
How to use CustomView in other ViewControllers, without having to copy code and or creating the following method in the class CustomView:
- (void)setData:(id)data;
because View does not know about the model.
I need to subclass a UITabBarController so that I can completely replace the UITabBar view with a custom view that I can hopefully produce in the interface builder. I tried but am not succeeding.
First, I created a subclass of UITabBarController along with a xib. I deleted the default view in the xib, and replaced it with a new one that was only 60px tall (the size of my tabbar). I dragged the necessary buttons onto it, and configured the .h file like so:
#interface ToolbarViewController : UITabBarController
#property (strong, nonatomic) IBOutlet UIView *tabBarView;
#property (strong, nonatomic) IBOutlet UIButton* firstButton;
#property (strong, nonatomic) IBOutlet UIButton* secondButton;
#end
My xib looks like this:
When I launch the app, I see an empty space at the bottom made for the tab bar, but I am not seeing an actual tab bar:
Update: I realize that I'm not actually launching the xib file in the .m file. Anyone know how I can do this properly?
There are various different solutions for adding a custom set of buttons to a custom tab bar controller subclass. I've done it years ago following this guide: http://idevrecipes.com/2010/12/16/raised-center-tab-bar-button/.
The idea is to add a custom UIView over the tab bar of your UITabBarController subclass. The CustomTabBarController class doesn't have to have a xib. Instead, I have a subclass of UIView that can either be programmatically laid out, or created using a xib for a UIView. Here's the header file for my CustomTabBarView class:
#interface CustomTabBarView : UIView
{
CALayer *opaqueBackground;
UIImageView *tabBG;
IBOutlet UIButton *button0;
IBOutlet UIButton *button1;
IBOutlet UIButton *button2;
NSArray *tabButtons;
int lastTab;
}
#property (nonatomic, weak) id delegate;
-(IBAction)didClickButton:(id)sender;
You'll either connect the desired buttons to button0, button1, button2, etc in the xib file, or do it programmatically on init for the view. Note that this is the UIView subclass.
In CustomTabBarView.m:
-(IBAction)didClickButton:(id)sender {
int pos = ((UIButton *)sender).tag;
// or some other way to figure out which tab button was pressed
[self.delegate setSelectedIndex:pos]; // switch to the correct view
}
Then in your CustomTabBarController class:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
tabView = [[CustomTabBarView alloc] init];
tabView.delegate = self;
tabView.frame = CGRectMake(0, self.view.frame.size.height-60, 320, 60);
[self.view addSubview:tabView];
}
When the buttons are clicked in the CustomTabBarView, it will call its delegate function, in this case the CustomTabBarController. The call is the same function as if you clicked on a tab button in the actual tab bar, so it will jump to the tabs if you have set up the CustomTabBarController correctly like a normal UITabBarController.
Oh, on a slightly separate note, the correct way to add a custom xib as the interface for a subclass of UIView:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
UIView *mainView = [subviewArray objectAtIndex:0];
//Just in case the size is different (you may or may not want this)
mainView.frame = self.bounds;
[self addSubview:mainView];
}
return self;
}
In the xib file, make sure the File's Owner has its Custom class set as CustomTabBarView.
Right now I have a custom view class called OTGMarkerDetailView which inherits from UIView and a corresponding .xib with it. It just has two text labels and I've linked the text labels to the text label IBOutlets in OTGMarkerDetailView.m.
OTGMarkerDetailsView.h:
#import <UIKit/UIKit.h>
#interface OTGMarkerDetailView : UIView
- (void)setLabelsWithMainAddress:(NSString *)mainAddress subAddress:(NSString *)subAddress;
#end
OTGMarkerDetailView.m
#import "OTGMarkerDetailView.h"
#interface OTGMarkerDetailView ()
#property (nonatomic, strong) IBOutlet UILabel *mainAddressLabel;
#property (nonatomic, strong) IBOutlet UILabel *subAddressLabel;
#end
#implementation OTGMarkerDetailView
- (void)setLabelsWithMainAddress:(NSString *)mainAddress subAddress:(NSString *)subAddress {
NSLog(#"%#", self.mainAddressLabel.text);
self.mainAddressLabel.text = mainAddress;
self.subAddressLabel.text = subAddress;
NSLog(#"%#", self.mainAddressLabel.text);
}
#end
I load it in another view as a subview, using initWithFrame. But the console always logs null when I try to set the text label values, and when I use a breakpoint it seems the mainAddressLabel and the subAddressLabel are nil themselves. Did I do something wrong in linking the xib to the view? What am I missing? Thanks.
I found a work around. I have created a custom UIView.
1.
I attached Nib file to it in initWithFrame method
CustomView *nibView;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
nibView = [array objectAtIndex:0];
[self addSubview:nibView];
}
return self;
}
You can see clearly, I haven't created the instance of UIView instead I created nibView of the same class type.
2.
Now creating IBOutlet properties and work on it. In customView.m file.
#interface FTEndorsedExpandedView : UIView
#property (retain) IBOutlet UILabel *label;
#end
3.
Create functions to set title or changing properties. (in customView.m file). Use nibView to access the properties rather than using self.label
-(void)setLabelText:(NSString*)string{
[nibView.label setText:string];
}
When you create your custom view in another view using initWithFrame a new instance of your custom class is created. This instance is not the same one you have in interface builder and hence the label properties are nil for this newly created instance. In order to solve this problem either put your view in its parent view in interface builder with its connection attached or override initWithFrame for your custom view and initialise your labels in there.
I've managed to setup a custom UIView class with a nib.
My .h looks like
#interface MyView : UIView <UITextFieldDelegate>
#property (nonatomic, weak) IBOutlet UITextField *textField;
#property (nonatomic, strong) MyView *topView;
And .m
#implementation MyView
NSString *_detail;
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])&&self.subviews.count==0){
MyView *v = [[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] objectAtIndex:0];
self.textField = v.textField;
if (self.topView == nil)self.topView = self;
v.topView = self.topView;
[self addSubview:v];
}
return self;
}
-(NSString *)topDetail{
return _detail;
}
-(NSString *)detail{
return [self.topView topDetail];
}
-(void)setTopDetail:(NSString *)detail{
_detail = detail;
}
-(void)setDetail:(NSString *)detail{
[self.topView setTopDetail:detail];
}
- (BOOL)textFieldShouldReturn{
//here I show an UIAlertView using self.detail for the message
}
Note: The setup I have works exactly how I want it to.
The problem
What I would like to do is remove my manual detail methods and turn NSString *_detail into #property (...)NSString *detail
When I try it with the #property, then within my ViewController if i call
myView.detail = someString, myView will be referring to the top most view. Then if textFieldShouldReturn gets called because of user interaction, then it calls the nested MyViews _detail which has not been set.
What I want:
To not have to write extra code for access to _detail regardless of where I'm accessing it from. I want to merely declare the property and go on with my usual coding.
Your problem is that you're trying to keep the a class reference, topView, with an object property.
In other words every objects' topView is the object itself, which makes no sense.
Your definition should be:
#interface MyView : UIView <UITextFieldDelegate>
// Class "properties"
+ (instancetype)topview;
+ (void)setTopView:(UIView *)topView;
// Object properties
#property (nonatomic, weak) IBOutlet UITextField *textField;
#property (nonatomic, strong) NSString *detail;
Now you can keep track of the topView:
static MyView * _topView;
#implementation MyView
+ (instancetype)topView {return _topView}; // You could also create one here lazily
+ (void)setTopView:(UIView *)topView { _topView = topView };
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])&&self.subviews.count==0){
JUITextFieldHint *v = [[[NSBundle mainBundle] loadNibNamed:#"JUITextFieldHint" owner:self options:nil] objectAtIndex:0];
self.textField = v.textField;
if ([MyView topView] == nil)[MyView setTopView:self];
v.topView = self.topView;
[self addSubview:v];
}
return self;
}
No more need for manual setters and getters. Now you can use your detail property, either with anyInstance.detail or [MyView topView].detail, or even MyView.topView.detail if you like dots like me ;)
You're init method still looks weird but should work. Check Apples init template.
Lastly, textField can be weak as long as it has a superview, otherwise make it strong.
My xib contained one UIView (no controller). I had the UIView set to MyView for the class.
I changed the UIView back to just UIView then set File's Owner to MyView. This solved issues of recursion (which is why I had such a weird setup in the first place) and caused my variables and IBOutlets to be linked up properly.
Credit goes to How do I create a custom iOS view class and instantiate multiple copies of it (in IB)? and some of the comments which I missed the first couple times I read through it.
I have two views. The first one is having 2 buttons and second one is having a label and a button. Am trying to change the text of the label based on the button pressed. In the code below, am calling an instance of second view and trying to change text in the label. But the problem is the text is not changing. Will appreciate if some one can help me here
#interface firstview : UIViewController {
IBOutlet UIButton *button1;
IBOutlet UIButton *button2;
}
#property(nonatomic, retain) IBOutlet UIButton *button1;
#property(nonatomic, retain) IBOutlet UIButton *button2;
-(IBAction)push:(UIButton *)sender;
#end
#import "firstview.h"
#import "secondview.h"
#implementation firstview
#synthesize button1;
#synthesize button2;
-(IBAction)push:(UIButton *)sender{
button1.tag = 1;
button2.tag = 2;
if(sender.tag == button1.tag){
secondview *v2 = [[secondview alloc]initWithNibName:#"secondview" bundle:Nil];
v2.title =#"first button";
v2.l1.text = #"BUTTON1";
[self.navigationController pushViewController:v2 animated:YES];
[v2 release];
}
else if(sender.tag == button2.tag){
secondview *v2 = [[secondview alloc]initWithNibName:#"secondview" bundle:Nil];
v2.title =#"Select";
v2.l1.text = #"BUTTON2";
[self.navigationController pushViewController:v2 animated:YES];
[v2 release];
}
}
#end
second view
#import <UIKit/UIKit.h>
#interface secondview : UIViewController {
IBOutlet UIButton *b2;
IBOutlet UILabel *l1;
}
#property(nonatomic, retain)IBOutlet UIButton *b2;
#property(nonatomic, retain)IBOutlet UILabel *l1;
-(IBAction)pop:(id)sender;
#end
#import "secondview.h"
#implementation secondview
#synthesize b2;
#synthesize l1;
-(IBAction)pop:(id)sender{
}
#end
At the time you are trying to set the label text, the view has not been loaded in your second view controller, so the label is nil.
Try moving the calls to after you push the view controller, or, better still (since only a view controller should change its views properties) have string properties on the second view controller for the label values, and set the label text value inside viewWillAppear.
From jrturton: The view is not been loaded in your second view controller, so the label is nil. What can you is declare a NSString property in secondview and you can set value of this property from firstview and then you can set this value to the label in viewWillAppear or viewDidLoad method.