I have this UIImageView in one of my view controllers
I've set it as a subclass of this cutsom UIImageView, ABProfileImageView
Here's that ProfileImageView class:
#import "ABProfileImageView.h"
#implementation ABProfileImageView
-(id)init {
self = [super initWithImage:[UIImage imageNamed:#"X4Hibreb.jpeg"]];
if (self) {
self.image = [UIImage imageNamed:#"X4Hibreb.jpeg"];
}
return self;
}
#end
and I have an image name X4Hibreb.jpeg in the project folder,
So I'm not sure why the imageview doesn't have the picture I set it to be? I think it's some higher understanding thing that I don't get, I usually do programmatic UI's, this is the first time I'm using a storyboard.
You have implemented init, however you also need to implement initWithCoder:, which is the initializer called when decoding objects from NIB files and storyboards.
Usually a good practice is to move common code from init and initWithCoder: to a _commonInit method and call it in the init.
You need to initialize the imageview.
Related
Simple question: what is the standard method of creating a customized version of say a UILabel, UIButton, etc. such that I can easily use it in multiple places? Is it simply to extend the appropriate class:
import UIKit
class FormField: UITextField {
override init()
{
super.init()
// borderStyle = UITextBorderStyle.None
}
}
Basically just want to get some default values set for some UI objects so I can easily drop them into the interface when necessary. Not really sure how to get this working.
It is very rare to subclass something like UILabel.
The most common approach is a HAS-A pattern, where you let a controller (often a UIViewController) manage the view for you and you reuse that. Alternately, you may make a UIView that contains the view you want to customize, and customizes it for you (passing along things that need to be passed along).
You can also have a "configure my view this way" function that you can call on an existing standard view. I haven't seen this very often in reusable code. My experience is that these kind of configuration functions turn out to be very app specific, but they're reasonable common.
Things like UITextField have delegate methods already, and a common way to customize them is to create a reusable delegate that applies certain behaviors.
It depends of course on what you're trying to achieve, but subclassing is pretty far down on the list of patterns, unless it's a class explicitly designed and documented to be subclassed (like UIView or UIViewController).
UIView and its subclasses have two designated initializers, -initWithFrame: and -initWithCoder:. The first is for programmatic instantiation while the latter is for views being unpacked from a Storyboard or NIB. Because of this, the common pattern for subclassing UIView subclasses is the following (I'm using ObjC but the Swift code should be easy to figure out):
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
// Do your special setup here!
}
Then, if you're using the views in Interface Builder, go to the Identity tab on the right-assistant-editor-sidebar, and in the top box where it says UILabel or UIButton, put in your custom button class name.
Hopefully this clears things up a bit.
I want to create a custom class RoundedImageView by subclassing the UIImageView class.
In order to make it round, I use the following code:
self.layer.cornerRadius = self.frame.size.width/2;
self.layer.masksToBounds = YES;
Where should I place this code? In the initializer? Or maybe in layoutSubviews? I know there's not a good idea to access properties from self within the initializer(because self may not be fully initialized), that is why I am confused and I'm not sure where to place this code.
The canonical initialization for a UIView (or any class derived from UIView) looks like this
- (void)setup
{
// do any initialization here
}
- (void)awakeFromNib
{
[self setup];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
[self setup];
return self;
}
This works for views that are created by storyboard and views that are created programmatically. Source: the CS193P videos.
The initialisation method is definitely the right place, since you want to set those properties only once.
About your concern related to acessing properties, the idea is that a property accessor could be overridden in a derived class and thus access portions of the object not already properly initialised. This is not a great concern in your case, I would say, unless you plan to override layer's getter in some not fully sensible ways.
I created a custom UITableViewCell (for this example, let's just say the subclass is MyViewCell) which has an Nib file associated to it MyViewCell.xib. The nib contains a UITableViewCell with one subview UIView (named cardContainer) that's simply a rectangle with a blue background. I want to add a drop shadow around the UIView, so I added set the layer properties in the -initWithCoder call:
#implementation MyViewCell
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self initView];
}
return self;
}
- (void) initView
{
UIBezier Path*shadowPath =[UIBezierPath bezierPathWithRect:view.bounds];
[self.cardContainer.layer setShadowColor: [UIColor blackColor].CGColor];
[self.cardContainer.layer setShadowOpacity: 0.8];
[self.cardContainer.layer setShadowRadius:3.0];
[self.cardContainer.layer setShadowOffset: CGSizeMake(2.0,2.0)];
view.layer.shadowPath = shadowPath.CGPath;
}
#end
The problem I'm having is that these layer properties aren't being drawn. If I call the -initView call within awakeFromNib or drawRect it's drawn as expected. My question: why doesn't my original code work? Where should I be calling initView? Is there some view lifecycle? I understand that the initWithCoder doesn't have the outlets connected, but it didn't crash at runtime.
I read through Apple documentation around Views and searched through the SO questions without finding an answer. I found this SO answer, but again doesn't explain.
Hey I found a better way to do this ,just add some runtime attributes for your subview cardContainer
like this
no more code in .m file anymore.
EDIT:
From:NSNibAwaking Protocol
Important: Because the order in which objects are instantiated from an archive is not guaranteed, your initialization methods should not send messages to other objects in the hierarchy. Messages to other objects can be sent safely from within awakeFromNib—by which time it’s assured that all the objects are unarchived and initialized (though not necessarily awakened, of course).
You need to add this,
self.cardContainer.layer.masksToBounds = NO;
Suppose you implement a custom table view and a custom view controller (which mostly mimics UITableViewControllers behaviour, but when initialized programmatically, ...
#interface Foo : MyCustomTableViewController ...
Foo *foo = [[Foo alloc] init];
... foo.view is kind of class MyCustomTableView instead of UITableView:
// MyCustomTableView.h
#protocol MyTableViewDelegate <NSObject, UITableViewDelegate>
// ...
#end
#protocol MyTableViewDataSource <NSObject, UITableViewDataSource>
// ...
#end
#interface MyCustomTableView : UITableView
// ...
#end
// MyCustomTableViewController.h
#interface MyCustomTableViewController : UIViewController
// ...
#end
How should you implement/override init methods in correct order/ways so that you could create and use an instance of MyCustomTableView both by subclassing MyCustomTableViewController programmatically or from any custom nib file by setting custom class type to MyCustomTableView in Interface Builder?
It important to note that this is exactly how UITableView (mostly UIKit for that matter) works right now: a developer could create and use either programmatically or by creating from nib, whether be it File owner's main view or some subview in a more complex hierarchy, just assign data source or delegate and you're good to go...
So far I managed to get this working if you subclass MyCustomTableViewController, where I will create an instance of MyCustomTableView and assign it to self.view in loadView method; but couldn't figure out how initWithNibName:bundle:, initWithCoder:, awakeFromNib, awakeAfterUsingCoder:, or whatever else operates. I am lost in life cycle chain and end up with a black view/screen each time.
Thanks.
It is a real mystery how the UITableViewController loads its table regardless of if one is hooked up in interface builder, however I have came up with a pretty good way to simulate that behavior.
I wanted to achieve this with a reusable view controller that contains a MKMapView, and I figured out a trick to make it happen by checking the background color of the view.
The reason this was hard is because any call to self.view caused the storyboard one to load or load a default UIView if didnt exist. There was no way to figure out if inbetween those 2 steps if the user really didn't set a view. So the trick is the one that comes from a storyboard has a color, the default one is nil color.
So now I have a mapViewController that can be used in code or in storyboard and doesn't even care if a map was set or not. Pretty cool.
- (void)viewDidLoad
{
[super viewDidLoad];
//magic to work without a view set in the storboard or in code.
//check if a view has been set in the storyboard, like what UITableViewController does.
//check if don't have a map view
if(![self.view isKindOfClass:[MKMapView class]]){
//check if the default view was loaded. Default view always has no background color.
if([self.view isKindOfClass:[UIView class]] && !self.view.backgroundColor){
//switch it for a map view
self.view = [[MKMapView alloc] initWithFrame:CGRectZero];
self.mapView.delegate = self;
}else{
[NSException raise:#"MapViewController didn't find a map view" format:#"Found a %#", self.view.class];
}
}
The strategy I've used when writing such classes has been to postpone my custom initialization code as late as possible. If I can wait for viewDidLoad or viewWillAppear to do any setup, and not write any custom code in init, initWithNibName:bundle: or similar methods I'll know that my object is initialized just like the parent class no mater what way it was instantiated. Frequently I manage to write my classes without any overrides of these init methods.
If I find that I need to put my initialization code in the init methods my strategy is to write just one version of my initialization code, put that in a separate method, and then override all the init methods. The overridden methods call the superclass version of themselves, check for success, then call my internal initialization method.
If these strategies fail, such that it really makes a difference what way an object of this class is instantiated, I'll write custom methods for each of the various init methods.
This is how I solved my own issue:
- (void)loadView
{
if (self.nibName) {
// although docs states "Your custom implementation of this method should not call super.", I am doing it instead of loading from nib manually, because I am too lazy ;-)
[super loadView];
}
else {
self.view = // ... whatever UIView you'd like to create
}
}
The problem is simple to explain but difficult for me to resolve. I have a property that is NEVER initialized.
First of all, I'm using the iCarousel custom class in order to display some images for my app. In one of its delegate methods (the one that it uses in order to know which view is going to show at some index), I use this code:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
if(!view)
{
CustomController* controller = [self.storyboard instantiateViewControllerWithIdentifier: "identifier"];
//BTW, the CustomController is initialized properly. Its instance is not nil after the initialization.
controller.imageView.image = [UIImage imageNamed: "something.png"];
view = controller.view;
}
return view;
}
As you can see, the view that I show in my carousel is a custom view with its own controller. I initialize it using the storyboard method and then I just set the image in my imageView property, which is, obviously, an UIImageView.
Don't get excited and say that I'm not initializing my imageView, because I have a custom getter in my "CustomController" class. Like this:
//interface (.h)
...
#property (nonatomic, strong) UIImageView* imageView;
...
//implementation (.m)
...
#synthesize imageView = _imageView;
...
- (UIImageView*) imageView
{
if(!_imageView)
_imageView = [[UIImageView alloc] init];
return _imageView;
}
...
Believe it or not, even if I put a breakpoint in the "_imageView = [[UIImageVIew alloc] init];"... the program executes that line but the _imageView remains nil. ¿Why?
I don't want to know "How to set my property", so please don't give workarounds for this... what I want to know is "Why my property is never setted and remains nil always", what's am I doing wrong?.
I've also tried to use my imageView as an IBOutlet... but even if I link it to an imageView in the Interface Builder and check its value after the "viewDidLoad", it still remains nil.
P.S: Btw, I'm using ARC (yeah, I know is in the title... xD)
Well, it looks like the answer was what borrrden said, the problem was the LLDB debugger. Actually, my property was initialized but the debugger didn't detect it like that, if I change it to GDB I could see it wasn't nil after all. Furthermore, the reason why I had also issues with my child viewcontroller's outlets was because I didn't use the View Controller Container methods in iOS5 (DidMoveParentViewController and those ones).
Kinda tricky.