I have a custom UITableViewCell and I need to initialize some ivars before it is displayed in a UITableView. I overrode - (void)init but that did not seem to be the designated initializer. Where should I be putting my code?
EDIT: the cell was made using UITableViewCell prototypes in Interface Builder.
- (id)initWithCoder:(NSCoder *)decoder;
Also, for what it sounds like you are wanting to do, you may instead want to override:
-(void)awakeFromNib;
– initWithStyle:reuseIdentifier: is UITableVIewCell's designated initializer
Related
CollectionCell is a UICollectionViewCell subclass. For learning purposes, the only difference is a single subview.
No interface builder. All in code.
I can do it like this for iOS 5…
[_collectionView registerClass:[CollectionCell class]
forCellWithReuseIdentifier:#"CollectionCell"];
…but it breaks in iOS 6, and I can't find any information.
Your subclassed CollectionCell doesn't have the "registerClass: forCellWithReuseIdentifier" method.
Your "UICollectionView" (which uses and displays CollectionCell objects) does.
Call "registerClass: forCellWithReuseIdentifier" on your collection view instead of the cell.
Make sure that your UICollectionViewCell classes extend PSUICollectionViewCell. I had the same issue happen to me and found out that my cells were extending PSTCollectionViewCell.
I am fiddling with the new UICollectionView and the UICollectionViewLayout classes. I have created a custom layout, subclassing UICollectionViewFlowLayout.
My cell sizes are changing dynamically and I set the item sizes using the delegate method below
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"SETTING SIZE FOR ITEM AT INDEX %d", indexPath.row);
return CGSizeMake(80, 80);
}
Now, under the prepareLayout method of my custom UICollectionViewFlowLayout class, I need to access these size variables so that I can make calculations how to place them and cache them for layoutAttributesForItemAtIndexPath.
However, I can't seem to find any property under UICollectionView or UICollectionViewFlowLayout to reach the custom item sizes I set in the delegate method.
Found it myself.
Implement the custom class like without omitting UICollectionViewDelegateFlowLayout
#interface SECollectionViewCustomLayout : UICollectionViewFlowLayout
<UICollectionViewDelegateFlowLayout>
and then you can call
CGSize size = [self collectionView:self.collectionView
layout:self
sizeForItemAtIndexPath:indexPath];
Looking at the various UICollectionView... header files, and watching the WWDC 2012 Session 219 - Advanced Collection Views and Building Custom Layouts video (from about 6:50 onwards), it seems the extensible delegate pattern takes advantage of dynamic typing to ensure the layout can properly access its extended delegate methods.
In short...
If you define a custom layout with its own delegate, define that delegate protocol in the layout's header file.
Your delegate object (typically the UI(Collection)ViewController that manages the collection view) should declare itself to support this custom protocol.
In the case that your layout is just a UICollectionViewFlowLayout or subclass thereof, this just means declaring conformance to UICollectionViewDelegateFlowLayout.
Feel free to do this in your class extension in the .m file if you'd rather not #import the layout header into the delegate's interface.
To access the delegate methods from the layout, call through to the collection view's delegate.
Use the layout's collectionView property, and cast the delegate to an object conforming to the required protocol to convince the compiler.
Don't forget to check that the delegate respondsToSelector: as usual prior to calling optional delegate methods. In fact, if you like, there's no harm in doing this for all methods, as the typecasting means there is no runtime guarantee the delegate will even implement the required methods.
In code...
So if you implement a custom layout that requires a delegate for some of its information, your header might look something like this:
#protocol CollectionViewDelegateCustomLayout <UICollectionViewDelegate>
- (BOOL)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath;
#end
#interface CustomLayout : UICollectionViewLayout
// ...
#end
Your delegate declares conformance (I've done so in the implementation file here):
#import "CustomLayout.h"
#interface MyCollectionViewController () <CollectionViewDelegateCustomLayout>
#end
#implementation
// ...
- (BOOL)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath
{
return [self canDoSomethingMindblowing];
}
// ...
#end
And in your layout's implementation, you access the method like this:
BOOL blowMind;
if ([self.collectionView.delegate respondsToSelector:#selecor(collectionView:layout:shouldDoSomethingMindblowingAtIndexPath:)]) {
blowMind = [(id<CollectionViewDelegateCustomLayout>)self.collectionView.delegate collectionView:self.collectionView
layout:self
shouldDoSomethingMindblowingAtIndexPath:indexPath];
} else {
// Perhaps the layout also has a property for this, if the delegate
// doesn't support dynamic layout properties...?
// blowMind = self.blowMind;
}
Note that it's safe to typecast here, as we're checking the delegate responds to that method beforehand anyway.
The evidence...
It's only speculation, but I suspect it is how Apple manages the UICollectionViewDelegateFlowLayout protocol.
There is no delegate property on the flow layout, so calls must go via the collection view's delegate.
UICollectionViewController does not publicly conform to extended flow layout delegate (and I doubt it does so in another private header).
UICollectionView's delegate property only declares conformance to the 'base' UICollectionViewDelegate protocol. Again, I doubt there is a private subclass/category of UICollectionView in use by the flow layout to prevent the need for typecasting. To add further weight to this point, Apple discourages subclassing UICollectionView at all in the docs (Collection View Programming Guide for iOS: Creating Custom Layouts):
Avoid subclassing UICollectionView. The collection view has little or no appearance of its own. Instead, it pulls all of its views from your data source object and all of the layout-related information from the layout object.
So there we go. Not complicated, but worth knowing how to do it paradigm-friendly way.
There is a swift version:
self.collectionView(self.collectionView, layout: self.collectionView.collectionViewLayout, sizeForItemAtIndexPath: indexPath)
Check out UICollectionView-FlowLayout on GitHub. Same idea, this just makes accessing the extended delegate methods of flowLayout a little cleaner.
For the later readers, IOS 7 has UICollectionViewFlowLayout which has defined it.
In my case everything about layout, cell layout etc. is being defined inside nib for UIViewController and separate nib for UICollectionViewCell. MyCollectionViewCell contains UIImageView with autolayout to cell with padding/margins but square-shaped.
I need round icons instead squared but don't want to take care which nib I use for iPhone or for iPad (I have separate nibs for devices and for orientation as well).
I don't want to implement #selector(collectionView:layout:sizeForItemAtIndexPath:) into my view controller.
So, inside collectionView:cellForItemAtIndexPath:
I can just use
CGSize size = cell.imageView.bounds.size;
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = size.height/2.0;
Because collectionView:layout:sizeForItemAtIndexPath: call before collectionView:cellForItemAtIndexPath: and layout done.
You can check round avatars on the bottom
I've been looking for a solution to make my NSTextField bottom-aligned and I've found this and adjusted it for my needs. So now I have this custom NSTextFieldCell but how do I tell my NSTextFields to use this class (programmatically)?
Since you ask how to do it programmatically, you can also use the setCellClass: method on your NSTextField subclass. Call it in the load or initialize class methods:
+(void)load
{
[self setCellClass:[MyTextFieldCell class]];
}
It will not have any bearing on your text fields defined in Interface Builder, as the text field cell set there takes precedence.
Have you tried setCell: method of the NSControl class?
- (void)setCell:(NSCell *)aCell
I'm trying to make a subclass of the UIBarButtonItem class. The button is added in the nib file and I set its class to my custom class in the interface builder. Now if this was a UIView class or subclass I would have override the - (id)initWithCoder:(NSCoder *)decoder method to start the extra customization, however UIBarButtonItem lacks such a method. I tried to override its -(id)init method but with no success, it doesn't get called. My question, where should I start my customization? What method do I need to override?
It's because you use IB. When you create an object in IB it does not call the init method for the class, it uses the archive version of the object. So to make custom initializations use this method instead:
-(void)awakeFromNib{
//initialize here
}
I have setup tab bar controller using interface builder, and each tab bar item is linked to a view controller (4 tabs, 4 view controllers). I want to know if Interface Builder uses an -init method to initialize the view controller because apparently this method does not get called:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
... and I want to do some initializations. I can't add that to -viewDidLoad since it is recalled in case of memory warning. Any idea?
Objects loaded from a *.(nib|xib) are inited with:
- (id)initWithCoder:(NSCoder *)inCoder;
So you could override that or if doing your setup after -initWithCoder: is called is not a problem you could use:
- (void)awakeFromNib;
from the NSNibAwaking protocol.
I was also going to mention initWithCoder vs awakeFromNib.
In general, I override initWithCoder when allocating memory for the object or setting values. When you need to do some setup after the IBOutlets are connected, then override awakeFromNib. Until then, IBOutlet instance variables to other views and controls are not connected.
Sounds like you want to implement -(void) awakeFromNib.
NSNibAwaking Protocol Reference (requires ADC login)