I'm getting a bit confused with UIViews recently, I have been using them fine up until this point and they are just refusing to be compliant!
I have a UIViewController, and this contains 5 different views. I have created IBOutlets for these views as I am wanting to swap them at runtime:
IBOutlet UIView *view1;
IBOutlet UIView *view2;
IBOutlet UIView *view3;
IBOutlet UIView *view4;
IBOutlet UIView *view5;
To make them easier to maintain I decided to keep them all within an array, called viewArray. Now I am trying to add the views to the array as follows:
viewArray = [NSArray arrayWithObjects:view1, view2, view3, view4, nil];
This is being called in the init function of my UIViewController class. I have linked up all the IBOutlets to their relevant views in the xib / interface file, but they do not appear to be initialized. Upon further debugging it looks like the views aren't initialized until after the init function is called?
So how can I create an array of these objects? I will need to select the relevant view before the view itself is shown, therefore viewDidLoad is not an option.
I know that you can grab the tags of things and implicitly set them using:
imageExample = (UIImageView *)[self.view viewWithTag:100];
But can this be used to find views, as surely it will be searching for the tags within the originally initialized view (view1)?
Thanks for any help in advanced,
Kind Regards,
Elliott
You can initialize the viewArray lazily, for the price of having to use self.viewArray instead of unqualified viewArray.
Here is how you can do it:
In the .h file:
NSArray* _viewArray;
#property (nonatomic, readonly) NSArray *viewArray;
In the .m file:
-(NSArray*) viewArray {
if (!_viewArray) {
_viewArray = [NSArray arrayWithObjects:view1, view2, view3, view4, nil];
}
return _viewArray;
}
Related
I'm having a problem that doesn't seem to have an obvious solution. I've searched around and I've been through all common answers that I could find.
My custom xib views don't show up on the app when I launch. The background is clear, and this xib has 5 image views as you can see below which aren't set to hidden.
The class has about 5 delegates as well which I set from a caller when the delegates have been initialized. The caller of initWithDelegates: is the parent UIViewController that displays this xib.
CustomView.h
#interface CustomView : UIView<SomeProtocol>
// UI Items - THESE AREN'T SHOWING UP
#property (strong, nonatomic) IBOutlet UIImageView *image1;
#property (strong, nonatomic) IBOutlet UIImageView *image2;
#property (strong, nonatomic) IBOutlet UIImageView *image3;
#property (strong, nonatomic) IBOutlet UIImageView *image4;
#property (strong, nonatomic) IBOutlet UILabel *label;
#property (strong, nonatomic) IBOutlet UIStackView *centerStackView;
- (id)initWithDelegates:(UIViewController*)delegate1 withDelegate2:(NSObject<Delegate2>*)delegate2 withDelegate3:(NSObject<Delegate3>*)delegate3 withDelegate4:(UIViewController<Delegate4>*)delegate4
withDelegate5:(NSObject<Delegate5>*)delegate5;
#end
CustomView.m
#implementation CustomView
- (void)awakeFromNib {
[super awakeFromNib];
}
- (id)initWithDelegates:(UIViewController*)delegate1 withDelegate2:(NSObject<Delegate2>*)delegate2 withDelegate3:(NSObject<Delegate3>*)delegate3 withDelegate4:(UIViewController<Delegate4>*)delegate4
withDelegate5:(NSObject<Delegate5>*)delegate5
{
NSArray * arr =[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:nil options:nil];
self = [arr firstObject];
self.delegate1 = delegate1;
self.delegate2 = delegate2;
self.delegate3 = delegate3;
self.delegate4 = delegate4;
self.delegate5 = delegate5;
[self setLoopImage];
[self layoutIfNeeded];
return self;
}
#end
What else I've verified:
I made sure that the xib file is properly selected under "Custom Class" in the interface builder.
I've walked through the code to make sure there are no crashes or obvious issues.
The xib doesn't appear to have any obvious issues in interface builder such as being set to HIDDEN.
The 5 IBOutlet ImageViews get a memory address and aren't nil.
Any help is appreciated!
UPDATE:
I believe this has something to do with putting the nibs in a nested UIView. In the interface builder, the order is UIViewController -> UIView -> nibs
There doesn't seem to be a method to register nibs from the parent UIViewController as there are in classes like UITableView.
I've also tried:
Setting the background color in the xib to make sure it shows up. It doesn't. However if I set the background color to red in the parent where I added the view in interface builder, it turns red. (See Figure 1), and my xib file is shown in Figure 2
Over-rode initWithFrame to make sure its being called. It is called.
Thinking that the view may be instantiated twice, one via autolayout and one via my custom init method, I tried making the (id)initWithDelegates method not init or register the nib in case that was duplicating the object. I did this by removing 1) return self, 2) the lines that register the nib, and 3) making the method prototype return void - I.E. (void)initWithDelegates - This didn't work either.
I tried these lines within initWithDelegates :
[self addSubview: self.image1];
[self bringSubviewToFront: self.image1];
[self.menuImage setHidden:image1];
-- with no luck.
Figure 1: (Actual result)
Figure 2 (Expected.. with red background)
Check your Autolayout in your custom XIB. Something the contain does not show because of it. For example: only center vertically & center horizontally. Try to do different autolayout constraint in your view in custom class.
I think something is wrong with layout of your CustomView inside ParentView. Could you please:
Check the frame of CustomView when you add it into the ParentView.
Check constraints that has CustomView inside ParentView. If you don't
have them - add it, constraints will iOS the glue how to layout your
CustomView. More information about this you can find here Programmatically Creating Constraints
And also you can try to use "Debug View Hierarchy" in Xcode to
inspect what is going on on your screen, there you can find your
CustomView in the view hierarchy and you will see what is wrong.
nibs cannot be nested within the structure.
Instead of:
UIViewController -> UIView -> nibs..
It should be:
UIViewController -> Container View -> UIView with nibs.
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'm using a scroll view with 3 views, and I need to place one label that shows up on each view,
the views are on separate view controllers: in view controller.h
#import "PagerViewController.h"
#interface ViewController : PagerViewController {
}
#property (strong, nonatomic) IBOutlet UIView *View1;
#property (strong, nonatomic) IBOutlet UIView *View2;
#property (strong, nonatomic) IBOutlet UIView *View3;
I've placed them as (in ViewController.m)
[self addChildViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"View1"]];
Should I fix an NSString
and Outlet to do this?
I need the labels to move with the scroll not to be separate from each other.
What you want, cannot be done with a single UILabel. You will have to create 3 separate labels and write the logic that will update all of them.
Create the UILabel outlets and write a method something similar to the method below:
- (void)updateLabels:(NSString *)text
{
self.label1.text = text;
self.label2.text = text;
self.label3.text = text;
}
This is just the way UIView's work and you cannot make them draw outside of their bounds.
I suggest you follow up on more iOS basics before going into complex layouts.
If you need to separate these views, a well-maintainable solution could be three separated label and one function (updateLabels:) in which the three label is updated to the same value.
I have 10 UIImageViews which do the same thing (they have some void methods that change their image with a timer).
My UIImageView is an outlet and I want to connect all the 10 imageViews to the same outlet, but interface builder doesn't allow me.
I found that there is a solution, IBOutletCollection. Can anyone explain to me how to use this to connect multiple imageViews to the same outlet?
Declare a property to hold your imageView's and then hook them up in interface builder like normal
#property (nonatomic, strong) IBOutletCollection(UIImageView) NSArray *imageViews;
it's just a normal NSArray but when the nib is loaded it will be populated with your imageView's
Update
In the header file for you view controller which has the multiple imageView's on you need to add the property above - it may look something like this:
#interface MyViewController : UIViewController
#property (nonatomic, strong) IBOutletCollection(UIImageView) NSArray *imageViews;
// other properties
#end
Now in the interface builder you connect all the imageView's to this one property.
Now I just work with the imageViews collection
for (UIImageView *imageView in self.imageViews) {
imageView.image = someImage;
}