TableCell init with coder and init with style - ios

I'm trying to save custom cell objects and later on show them. But fail to understand the mechanism totally i have:
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
self.action = [decoder decodeObjectForKey:#"action"];
}
return self;
}
and
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
this way I get my variable decoded but the tablecell doesn't show up at all. if I remove init with coder i do get my tablecell interface showed, but can't get variable decoded. What should I do to make this work?

I think i can see your problem.
still, you shouldnt save the cells themselves. just create a UITableViewCell-Subclass that accepts one object from which it will gather all of its content. if you need more than one class for this, thats fine too. You can easily create several subclasses for several usecases.
then just save the data-objects and recreate the cells when you need them again.
the reason for this is that your way creates massive overhead, because you are trying to save all the UIviews, Lables, etc that make up your cell. Also, UITableVIews are highly optimized and try to reuse existing cells which does not play well with your approach.
TL;DR:
You need dataobjects, which you can save in keyedArchivers, and you need tableviewcells that accept these objects and display the necessary data. Seriously, you will save yourself some trouble. Let me know if you need help and have fun

Related

Conceptual q about custom UIKit objects

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.

UIImageView subclassing

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.

Is there a init method in iOS that is always called?

Is there a method that is always called in Cocoa? Many classes have init or initWith, but even worse they can be loaded from a nib or something. I don't want to have to scrape around and find how it does this in this case. I just want to set some initial variables and other things, and I want a method to subclass that I can depend on no matter if it's a UIView, UIViewController or UITableViewCell etc.
No there is not such a method. init comes from NSObject so every object can use it, and as well subclasses define their own initialization methods. UIView, for example, defines initWithFrame: and furthermore there are init methods from protocols, such as NSCoding which defines initWithCoder:. This is the dynamic nature of objective-C, anything can be extended at any time. That being said, there are some patterns. UIViewController almost always takes initWithNibName:bundle: and UIView almost always takes initWithFrame: or initWithCoder:. What I do is make an internal initialize method, and just have the other inits call it.
- (void)initialize
{
//Do stuff
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self initialize];
}
}
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if(self)
{
[self initialize];
}
}
Not 100% sure that it is always called, but I am pretty sure that this is a viable option. To be perfectly honest, I can't recall that I have ever seen this method used in practice and I usually shy away from using this method (I have absolutely no idea why, probably because it's just not the cleanest and most comprehensive method to achieve this...):
-didMoveToSuperview()
From documentation:
Tells the view that its superview changed.
The default implementation of this method does nothing. Subclasses can override it to perform additional actions whenever the superview changes.
There's many ways you can write a custom initializer.
- (id)initWithString:(NSString *)string {
if((self == [super init])) {
self.string = string;
}
return self;
}
That's just how I write my initializers in general. For example, the one above takes a string. (you don't have to pass strings if you don't want).
Btw, init is a method. According to the header for NSObject, init has a method implementation.

Subclassed UILabel stored within serialized view not storing custom vars

So, I have a view that is being serialized and stored in a file. Within that view, is n subclassed UILabels, with the only difference being a live property. I have the initwithcoder and encodewithcoder within the UILabel subclass, but I am still unable to get the custom variable within the label. I have included my subclass' methods and the below. Any help is appreciated.
Custom UILabel:
- (id) initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self != nil) {
self.live = [decoder decodeBoolForKey:#"live"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeBool:self.live forKey:#"live"];
}
Because I am only unarchiving the view, which contains the labels within, I assume that ios does not unarchive the custom labels?
Thanks
You are missing a call to [super encodeWithCoder:aCoder]. This will result in an incorrectly serialised object, I'm not sure what the consequences are, but you seem to have found one of them!

Static content in UITableView - Problems with Apples guide

I have been following the guide from Apple:
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7
concerning: Technique for Static Row Content.
My problem is the following:
All my cells are nil.
According to Apple, the loading of the view should instantiate all object populating that nib file, bit only the tableview is instantiated. Thus the cell are nil and crashes the program in method cellForRowAtIndexPath: when it is returning a NULL cell.
I have followed the guide to the letter, search all over and have found no solution, except creating a nib for each cell and loading those ones when needed, but that solution is much less elegant than the Apple solution.
Any help would be appreciated.
You have probably not bound your IBOutlets for the cells to the cells themselves. If your cell ivars are nil, then you didn't wire something.
EDIT
If the IBOutlets are connected, but the ivars are nil, then the IBOutlets aren't connected. There other possibilities, but that's the most common. Other possibilities: you're trying to access prior to viewDidLoad. In particular, you can't access your ivars during initWithFrame:. Other possibility is that you're overwriting them at some point. Check their values in viewDidLoad. Other possibility is that you're loading the wrong nib file, or not loading a nib file at all. All possibilities, but I'd first check everything in viewDidLoad and double-check you're connections.
When instantiating your table view controller, in your init method you should call the overridden designated initializer of the superclass ([UIViewController initWithNibName:bundle:]), passing in the nib name:
- (id)init {
return [self initWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization here
}
return self;
}
For more details, see this answer: Automatically Loading XIB for UITableViewController

Resources