I recently started playing with the awesome UICollectionView API, making reasonable progress, but have been stuck for almost all day with an issue I'm hoping someone can help me with:
I need to add some custom details to certain cells' attributes.
In order to do this, the right approach seem to be to subclass UICollectionViewLayoutAttributes and add the properties I need to my subclass. So far so good, except that when I return my LayoutAttributesSubclass, I always get the, somehow obscure, following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** setObjectForKey: key cannot be nil'
Having tried to track this down for a while, I'm under the impression that the error is related to representedElementKind and representedElementCategory being nil in my subclass's instance. But those properties are read only, so I can't set those.
I've managed to somehow bypass the error by cheating, getting a regular UICollectionViewAttributes instance, then changing it to a LayoutAttributesSubclass using object_setClass, but this then raises a ton of other issues, plus seems rather shady to me.
In short, does anyone knows what the error above means, and how to correctly create/use UICollectionViewLayoutAttributes subclasses?
When setting up custom attributes, you need to subclass UICollectionViewLayoutAttributes and subclass UICollectionViewLayout and "declare" your custom attribute subclass class name by overriding +(Class)layoutAttributesClass in your UICollectionViewLayout class. The system calls this class method to see if there is a custom class to be supplied when you use the factory method for instantiating/dequeuing layout attribute objects.
#interface YourCustomCollectionViewAttributes : UICollectionViewLayoutAttributes
#property (nonatomic) UIEdgeInsets myCustomProperty
#end
#interface YourCustomCollectionViewLayout : UICollectionViewLayout
#end
#implementation YourCustomCollectionViewLayout
+ (Class)layoutAttributesClass
{
return [IRTableCollectionViewLayoutAttributes class];
}
#end
This is correct according to the documentation and should prevent the particular error you are having. Also when you implement custom iVars, be sure to implement an override for -(id)copyWithZone: or the UICollectionView will lose any custom values you have applied to your custom collection view object.
Related
After updating to iOS8.3 I started getting a bunch of new warnings that werent there on iOS8.2. One in particular that caught my eye;
#property (strong, nonatomic) IBOutlet UITableView *tableView;
which was declared in a '.m' file.
What has changed in iOS8.3 to make this a warning?
Auto property synthesis will not synthesize property 'tableView'; it will be implemented by its superclass, use #dynamic to acknowledge intention
If you're using a UITableViewController then tableView is already synthesized. (ie. self.tableView is the tableView of the UITableViewController).
I faced similar issue too. I solved this by the following method. Inside your .m file write #dynamic tableView under the #implementation
I hope your issue will be solved.
What has changed? The compiler has become more clever.
You are probably subclassing UITableViewController.
UITableViewController already has a property named tableView. It is already synthesized or implemented otherwise in UITableViewController. So the warning tells you that you are not getting your own tableView property, but that you are getting the one supplied by UITableViewController.
Obviously if you were not aware of the tableView in UITableViewController, and if you wrongly assumed that this is your property, under your control, there would be trouble. That's why you get a warning. So if that is what you were doing, then your code was always badly broken, and needs fixing.
But if you just have the #property declaration in your code, but you know that it is actually the UITableViewController property, no harm is done, but remove the #property because it is wrong.
Had a similar problem with a custom UITableViewCell creating a new property called imageView. Since a property named imageView already existed, I kept getting the error message. I simply changed the name to projectImageView and it worked.
Answers to similar questions did not help so I'm opening a new question.
I have this issue in several of my projects now:
Tapping on the first responder UITextField or on a UITextView (that would normally bring up the default context menu for copy and paste) causes my app(s) to crash.
Note: UICalloutBarButton is a UIKit private API, so there is no chance to change its layoutSubviews implementation and call [super layoutSubviews] in it.
Log message:
*** Assertion failure in -[UICalloutBarButton layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2935.137/UIView.m:8794
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UICalloutBarButton's implementation of -layoutSubviews needs to call super.'
I don't have any idea why this appears. Would be very happy for any help.
EDIT
Trying to reproduce the crash in a demo project I found out that it is a custom UIView category that causes the crash. See accepted answer for more details.
By trying to reproduce the crash in a demo project I found out that it is a custom UIView category that causes the crash.
When creating this fault, I assumed that category methods were only available for UIView subclasses that #import this category.
But this is not the case as described in the Apple Documentation:
Any methods that you declare in a category will be available to all instances of the original class, as well as any subclasses of the original class. At runtime, there’s no difference between a method added by a category and one that is implemented by the original class.
So not being aware of this, I had overridden in my custom category the UIView method + (BOOL) requiresConstraintBasedLayout to return YES (like I wrongly thought only for those classes that import this category).
Putting a breakpoint in that method, I learned that this 'category-method' gets called from everywhere in the UIKit once the category files are added to a project.
Nothing but a painful way of learning/accepting, that overriding methods in categories is never a good idea - unless you want to cause strange crashes like this ;-).
Sorry for stealing your time, but thank you for asking some code examples which forced me to seriously reproduce the problem - which lead me to the crash-cause...
My app subclasses UICollectionViewFlowLayout and uses all of its properties except for minimumLineSpacing. To avoid confusion, I'd like to be able to "hide" minimumLineSpacing from the outside, so it looks like my subclass doesn't even support it. Is this possible?
Yes you can. Kind of. You can mark it with __attribute__((unavailable)), which will cause the compiler to throw an error if you use it. However, the property will still be accessible if your object is cast to its superclass type, as this is a compile-time-only thing.
#interface MyClass : UICollectionViewFlowLayout
#property (nonatomic) CGFloat minimumLineSpacing __attribute__((unavailable));
#end
I don't think you can actually hide it. You could of course overwrite the getter and setter and prevent the acutal value from beeing changed, if that is of importance. But they will always exist and be visible.
I want my program to be like this
if ([UIImageViewObject identifier]==#"heyItsMeYeaCoolDude")
{
do some methods
}
Whenever I do this though my programs crashes saying:
2012-07-29 19:09:58.401 Bridges[2711:f803] -[UIImageView identifier]: unrecognized selector sent to instance 0x8874f70
With a bunch of crap after it.
In my storyboard, I typed heyItsMeYeaCoolDude under label in the identity inspector, and now my program is complaining.
I know I could use tags also, but I'm already using my images tag for something else, help!
In response to the suggestion you made in the comment below your question, #ownageGuy, an answer to the question you pose:
You're welcome to subclass anything you like. Subclassing UIImageView is a perfectly acceptable solution to this problem. You might also consider the container pattern; ie. create a class which contains an instance of a UIImageView and an NSString for the identifier. Then even if there did happen to be a detriment to subclassing UIImageView, as you fear, it would be eliminated.
#interface UIImageViewContainer : NSObject
{
UIImageView* imgView;
NSString* identifier;
}
Then you create an object of type UIImageViewContainer, set imgView to the appropriate UIImageView, and store the identifier string inside identifier.
This looks like a basic IB error but it hasn't worked out to be.
*** Terminating app due to uncaught execption 'NSUnknownKeyExecption', reason:'[<TrailersViewController 0x585e8e0> setValue:forUndfinedKey:]: this class is not key value coding-compliant for the key myImageView.'
myImageView isn't in TrailersViewController it's in
another object that is on TrailersViewController.
myImageView does not have any IBOutlets.
Third it seems that you can have this problem when pushing view
controllers but it's object is shown in a subview.
myImageView is just a local variable in the object.
Also I had this working but noticed my TrailersViewController was rather leaky so I'm re-organizing it to stop those leaks.
I'm at a loss at the moment but any other directions I could head in would be awesome.
It seems like you have associated the myImageView view in the nib to a #property IBOutlet called myImageView inside TrailersViewController, but you have forgot to #synthesize this property.
So when the runtime tries to assign the myImageView object loaded from nib to the myImageView instance using key-value approach it doesn't find the setter method has you didn't synthesize it.