I made a custom UIView object customView
The main initializer i used during prototyping was:
public init(textString: String, frame: CGRect) {
Once I want to add this little guy to the IB I'm not sure how to get it working. I understand that once I add the function to the Interface builder it calls:
required public init(coder aDecoder: NSCoder)
I'm at a loss at this point how to get my custom textString passed into the initializer
Is there a process to do this?
Your Custom UIView has two init methods which are :
initWithFrame:frame
initWithCoder:aDecoder
You do not use this two methods to pass data like your textString. Instead textString should be a property of your view. This property will be initialized via a View Controller that owns your custom view.
#interface CustomView : UIView
#property (strong, nonatomic) NSString *textString;
#end
Then you set the textString property's value in the View Controller, for instance in its viewDidLoad method.
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet CustomView *myCustomView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.myCustomView.textString = #"There's my textString";
}
#end
Hope this could help.
Make your custom view #IBDesignable, and the text property #IBInspectable, then you can set the text from inside the storyboard/xib file.
Related
So I've created a custom UIView subclass and have it assigned to a UIView in my main storyboard. When the view loads everything is displayed properly.
The issue I'm having is that I need to be able to access properties of said custom UIView since the view is data driven.
JSON_table.h:
#import <UIKit/UIKit.h>
#interface JSON_table : UIView
#property (strong, nonatomic) IBOutlet UIView *view;
#property (weak, nonatomic) IBOutlet UISearchBar *searchbar;
#property (weak, nonatomic) IBOutlet UITableView *table_view;
#property (weak, nonatomic) NSString *data_header;
#property (weak, nonatomic) NSString *data_list;
#end
JSON_table.m:
#import "JSON_table.h"
#implementation JSON_table
- (id) initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
[[NSBundle mainBundle] loadNibNamed:#"JSON_table" owner:self options:nil];
[self addSubview:self.view];
}
return self;
}
#end
(I know I'm missing delegates for tableview, ill be adding these later)
The issue I'm having is when I right click on my UIView on my storyboard I get:
The problem is when I try to connect "view" to my header file "
ViewController.h" it doesn't let me create a IBOutlet, so I cannot reference my view and its properties in code.
This is what I am trying to accomplish:
"Table" is of type UIView
Idea:
Would this have anything to do with the UIView being on the second view in my storyboard? I noticed that I don't seem to have any problem attaching to anything on the first page, but the second one I can't.
You can only connect the outlets of a view to it's class object. You are trying to connect outlets of JSON_table object to UIViewController object.
If you need to access those properties in UIViewController object. You need to import
JSON_table.h
in your view controller. And create and instantiate a object of it.
JSON_table * customView = [[JSON_table alloc]init];
Now you can access all the properties of it as:
customView.searchbar, customView.view etc.
Added by theshadow124:
Thanks to everyone who attempted to help me solve my problem. Due to being fairly new to coding for iOS I didn't realize I had to assign a custom class to every UIViewController in my storyboard(I thought they they would inherit from the base if I didn't specify). simply creating a new subclass of UIViewController and assigning it under the Identity inspector fixed my problem and now I can properly assign outlets.
Im going to accept this answer because it was one of the issues I ran into after fixing the subclass on the storyboard issue.
Please make sure that in assistant editor your are opening the same class that your custom class is contained in .
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 created a subclass of UITextField to override the method rightViewRectForBounds.
Here is my custom class CustomTextField
#interface CustomTextField : UITextField
#end
#implementation CustomTextField
// override rightViewRectForBounds method:
- (CGRect)rightViewRectForBounds:(CGRect)bounds{
NSLog(#"rightViewRectForBounds");
CGRect rightBounds = CGRectMake(bounds.origin.x + 10, 0, 30, 44);
return rightBounds ;
}
#end
Now I set up my ViewController to call the custom class instead of UITextField.
#import "OutputViewController.h"
#import "CustomTextField.h"
#interface OutputViewController () <UITextFieldDelegate>
#property (strong, nonatomic) IBOutlet CustomTextField *field1;
#property (strong, nonatomic) IBOutlet UILabel *label1;
- (void)methodName
{
self.field1.rightView = self.label1;
}
The property rightView should call my method override according to Apple's Documentation: "The right overlay view is placed in the rectangle returned by the rightViewRectForBounds: method of the receiver". Why isn't my override working?
Sorry if this is a bad question. I've only been programming for a month.
The problem is most likely that field1 is not in fact a CustomTextField. It is easy to confirm this with a breakpoint or some logging.
Remember, it is not enough to declare the class of something. That thing must actually be that class (polymorphism). An instance has a class, quite without regard for how you may cast or declare a variable that refers to it.
I prefer to create custom views for all my view controllers. And I define it in code by using weak references for custom views like this:
#import "MyViewController.h"
#import "MyCustomView.h"
#interface MyViewController ()
#property (nonatomic, weak) MyCustomView *customView;
#end
#implementation MyViewController
- (void) loadView
{
MyCustomView *view = [MyCustomView new];
self.view = view;
self.customView = view;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// work with custom view
self.customView.tableView.delegate = self;
....
}
#end
Is this the correct use of weak references? Will the application crash or leak, or will there be other problems?
In this case weak is fine. You assign your CustomView to self.view which is defined in the UIViewController header as
#property(nonatomic,retain) UIView *view;
so the view property has a retaining reference.
There is a possibility that your view and customView could get out of sync - so I would be tempted to define customView as readonly and implement the getter as
- (CustomView *)customView
{
return (id)self.view;
}
As you can see in the documentation of UIViewController the view controller's view property has a strong reference to the view. So the custom view object will be retained as long as you don't set the view property to something else. In short, your method works.
As you create the instance from within this controller programatically, you should use a strong reference to set the ownership clearly to this controller.
In the event that you create the view object in IB or soryboard respectively, then a weak reference to the related IBOutlet would do.
I have a CustomView UIView class that represents my screenUI in which I programatically create and position the UI elements, among which multiple UITextFields and a UITableView.
I also have a UIViewController and in it's loadView which I assign my CustomView to self.view.
I do this in order to best keep the cohesion of the view and the controller.
My question is how can I set the delegate of the UITextFields and UITableView inside the CustomView to the managing UIViewController?
It is my understanding that the UITextFieldDelegate and all other protocol methods should be implemented in the Controller and not the View, but I would like to avoid setting the delegate of the items in the view controller (e.g. textField1.delegate = self in the UIViewController class instead of textField1.delegate = <value for setting UIViewController as delegate> in my CustomView class) in order to keep my code cleaner.
Thank yous.
You could have a delegate property on your custom view:
customView.delegate = self;
self.view = customView;
In the delegate setter you could forward responsibilities:
-(void) setDelegate:(id<*the protocols you need*>) del
{
_textField.delegate = del;
...
}
You could have more than 1 type of delegate on your custom view, it depends on you needs, but one implementing all the protocols should usually suffice.
I would probably just forward them along. Create properties representing the delegates on your custom view. then in loadView set them to self.
// CustomView.h
#property (nonatomic, weak) id <UITextFieldDelegate> textFieldDelegate;
#property (nonatomic, weak) id <UITableViewDatasource> tableViewDataSource;
#property (nonatomic, weak) id <UITableViewDelegate> tableViewDelegate;
// Create textField
textField.delegate = self.textFieldDelegate;
// in ViewController
- (void)loadView
{
CustomView *view = //...
view.textFieldDelegate = self;
}