NSCollectionView inside another NSCollectionView - objective-c

I have these two classes:
#interface Father : NSObject
{
NSString* name;
NSArray* listChildren;
}
#property (copy, readwrite) NSString* name;
#property (copy, readwrite) NSArray* listChildren;
#end
#interface Child : NSObject
{
NSString* nameChild;
NSImage* picture;
}
#property (copy, readwrite) NSString* nameChild;
#property (copy, readwrite) NSImage* picture;
#end
I'm trying to make a NSCollectionView filled with Father items, and for each father item's View i will have a name label, and another NSCollectionView filled with the (father) representedObject.listChildren items.
I've managed to create an external NIB file for the father NSCollectionViewItem's View to make things easier, but I'm not able to bind the child CollectionView to the representedObject.listChildren property. Actually, there is no problem in binding in IB, and at runtime the system is actually calling the property (I've added and getListChildren implementation and a NSLog call to make sure the property is being called). It seems that the inner CollectionView's won't load the items found in my NSArray* property?
It is driving me crazy, any ideas about what is going on?
Help please!!

I had the exact same problem and I found the solution!
I'm a complete novice to Objective C and Cocoa so I don't fully understand the reasons why this does not work exactly. Maybe somebody else can enlighten us.
In my first try I simply did everything in the default MainMenu.xib. You end up with two NSArrayController this way. Now apparently, as you suspected, the issue lies with the second NSArrayController for the inner items. It somehow doesn't get "copied" correctly. Extracting each NSView into its own .xib solves this issue.
Actually this discussion got me started in the right direction. I later discovered/understood that this is the basically the same idea #user493638 already hinted at.
Combining this knowledge with the tutorial here, on how to extract the views into their own .xib solved the problem for me!
Again I don't understand Objective C and Cocoa nearly enough to fully appreciate the underlying reasons for this behavior, who knows how exactly all this binding magic works under the hood...

Related

How should I document property setters?

I occasionally override the setters of Objective-C properties, and was wondering. If I change the default behavior of a method drastically, where should I document this in the header? Or should I just use a new method completely?
In my current case, I am setting a view up as a placeholder view in interface builder. Programatically, there will be a way to replace this view with a new view (Either an icon, or an arbitrary custom view). The method to swap out the placeholder view will automatically set the property to the new view, remove the placeholder view from the parent view, add the new view to the parent view, and reposition/resize the new view appropriately.
I came up with three options:
A) Override the setter of the property, and document along with the property:
// Documentation goes here
// The setter of this property actually does <etc>
#property (nonatomic, retain) IBOutlet UIView* placeholderView;
B) Override the setter, and declare it in the header:
// Documentation goes here
-(void)setPlaceholderView:(UIView*)view;
C) Use a completely different method and set the property to readonly:
#property (nonatomic, retain, readonly) IBOutlet UIView* placeholderView;
-(void)replacePlaceholderView:(UIView*)view;
Option C seems appealing, because it makes it quite clear what the method does. It will also be clear that since it is different than a normal setter, it may act differently (which it will). The disadvantage I see here is that it doesn't seem to follow the normal Objective-C trend.
What do you guys think is the cleanest way to do something like this?
You shouldn't "change the default behavior of a [setter] method drastically" at all. A setter method should set a property, and if it does, you don't need to document the override in the header anywhere, since it does exactly what the user would expect, and the override is an implementation detail that can be documented in the implementation file. Radically changing expected behavior is only going to sow confusion, and is something to avoid. Go with option (C) unless your desired behavior is what a user would reasonably expect to happen when she sets that property.
In practice, an IBOutlet should only be set during initialization from nib files. If I was to refactor this, I'll declare the properties
#property (nonatomic, weak) IBOutlet UIView *initialPlaceholderView;
#property (nonatomic, weak) UIView *placeholderContainerView;
#property (nonatomic, weak) UIView *currentPlaceholderView;
then rename replacePlaceholderView: to
-(void)updatePlaceholderContainerWithNewView:(UIView*)newPlaceholderView;
This is self-documenting code. You can already assume the behavior just by the property and method names.

Reloading UITableView from another class?

This is a question rather many people have asked, especially here on StackOverflow.
Reloading the data on the table view is easy, [self.myTableView reloadData];, myTableView is the instance of my UITableView, since I am using a UIViewController instead of a UITableViewController.
I want to reload the table view from another view controller after I have updated the data (from Internet). The data is contained in a property list. I have tried using protocols, notifications and some other things like putting it in viewDidAppear:. Nothing have worked for me.
Is it something I haven't thought about or have I just done some of the methods wrong? The help is much appreciated!
If you want access to a UITableView object, or just about anything else for that matter, from another class, you can make it accessible using a property:
ClassA.h
#interface ClassA {
UITableView *tableView;
}
#property (nonatomic, readonly) UITableView *tableView;
#end
ClassB.m
- (void)reloadTableInOtherClass {
[classAVariable.tableView reloadData];
}
Using observer pattern + notification is a good way. And to let your view controller decide when to reload data is also a good practice.
Why notification does not work. Did you use addOvserver: ?

Declaring IBOutlet inside or outside #interface?

sorry If am I being too picky on this one but I am learning iOS programming now and I've seem some people who declare the IBOutlet like this:
IBOutlet attached to property
#import <UIKit/UIKit.h>
#import "CustomCell.h"
#interface CustomTableViewController : UITableViewController {
CustomCell *customCell;
}
#property (nonatomic, retain) IBOutlet CustomCell *customCell;
#end
And some declaring like this:
IBOutlet attached to the declaration inside the interface
#import <UIKit/UIKit.h>
#import "CustomCell.h"
#interface CustomTableViewController : UITableViewController {
IBOutlet CustomCell *customCell;
}
#property (nonatomic, retain) CustomCell *customCell;
#end
which one is the proper way to declare it? Are any differences between them?
If someone know to explain why do they put it on different places it would be awesome to learn.
Thanks a lot :)
Both of those are still "inside the interface" so your title it a bit confusing but I see what you are asking.
In many cases the result of either approach will be the same but they are different. An IBOutlet property will call the property's setter method which gives you an opportunity to override that setter if setting that property should have some side effect.
I prefer to use outlets on properties because I think it makes the memory management of the objects loaded from the nib much clearer. Take a look at memory management of nib objects and I think you will see what I mean.
Objects in the nib file are created with a retain count of 1 and then autoreleased. As it rebuilds the object hierarchy, UIKit reestablishes connections between the objects using setValue:forKey:, which uses the available setter method or retains the object by default if no setter method is available. This means that (assuming you follow the pattern shown in “Outlets”) any object for which you have an outlet remains valid. If there are any top-level objects you do not store in outlets, however, you must retain either the array returned by the loadNibNamed:owner:options: method or the objects inside the array to prevent those objects from being released prematurely.
IBOutlet ivars will call setters for those ivars if they exists and directly retain the object loaded from the nib if no setter is found.
Advertising the property as the IBOutlet at least makes it clear that the property's setter will always be used and follow whatever memory management rule has been set for that property.
Finally I argue that IBOutlets are part of the public interface of a class and it is therefore better to expose methods (via a property) for working with them eager than using -setValue:forKey: to manipulate the backing ivars which should be an implementation detail.
The two styles are interchangeable, there is no difference in the generated code or the way objects will be loaded from a nib. Really.
However, both styles have a redundant line. Simply leave out the ivar declaration. Just the line
#property (nonatomic, retain) IBOutlet CustomCell *customCell;
is sufficient in the modern runtime.
If you have a complex project, I suggest moving all the outlets out of the public interface into a separate header file. Most outlets are private interface, the only reason to have them in a header is so Interface Builder can find them.
You can declare both ways, there is no difference actually.
But, here is the thing:
If you need your class to have some ivar with a special behavior or it has to be accessed from outside, etc, and it has to be a property, then I will say you have 2 options to choose from (attached to the property and inside the class interface).
If that is not your case, don't create a property, is not necessary, just do it inside your class interface.
Hope it helps ;)

Why "error: expected specifier-qualifier-list before 'UITextView'" (but not for other view types!)

A question about specifier-qualifier-list has already been (asked) and answered but none of the anwsers given solve my problem.
My code is simple:
#import <UIKit/UIKit.h>
#interface TableCellWithTextView: UITableViewCell
{
#private
UITextView *theTextView;
}
#property (nonatomic, retain) UITextView *theTextView;
#end
But I get the compiler error "error: expected specifier-qualifier-list before 'UITextView'"
If I replace UITextView with UIView the error goes away. More oddly in my um.. in my view (no pun intended ;-)) is that I can also use any other view class without problem (UITextLabel, UIImageView, etc..)
But most bizarrely of all, elsewhere in my project, this compiles (and works):
#import <UIKit/UIKit.h>
#interface ToDoDetailTextViewEditCell : UITableViewCell
{
#private
UITextView *theTextView;
}
#property (nonatomic, retain) UITextView *theTextView;
#end
OK, people don't trouble yourselves. Things got weird for me as I dragged the files over to a brand new project and everything compiled. And ultimately weird when I deleted the old copies, dragged them back in from the new 'temp' project and everything compiled again.
So, no idea what the problem was but something must have got messed up. The files were part of a brand new folder structure I'd created by hand and I'd also been doing some refactoring, renaming files, so I guess some references somewhere had just got screwed up. I tried cleaning, many times, to no avail. What I didn't try actually was to restart Xcode (though I kept thinking to do it).
But egal. It works now.

Setting a property on a custom object through Interface Builder

I have a custom UITableViewController subclass which I use in two places in a nib file. What should I do if I want the two instances to have slightly different behavior? Of course in the code I can select one kind of behavior or the other based on the value of a BOOL, but how do I set that BOOL from Interface Builder, without having to write an Interface Builder plugin?
As of Xcode 6 there is a new way doing this. You can now give your view properties the attribute IBInspectable and then you can edit those properties in IB as you would with and standard view.
So for example:
#property (nonatomic, strong) IBInspectable BOOL
More details (also for the new attribute IBDesignable) in Apples documentation: https://developer.apple.com/library/ios/recipes/xcode_help-IB_objects_media/chapters/CreatingaLiveViewofaCustomObject.html
"User Defined Runtime Attributes" in the Identity inspector is probably what you're looking for. This seems to be new as of Xcode 4.2.
Unfortunately, there doesn't seem to be much (any?) documentation about this feature on the Apple Developer site. I was able to use it for a simple property set.
So far as I know, you can't set parameters in IB without writing an IB Plugin.
That said, you have two other options.
If it is as simple as a single BOOL, you're probably best off making it a property of the MyCustomViewController class and set it in code after you init:
customViewController = [[MyCustomViewController alloc]initWithNibName:#"CustomViewController" bundle:nil];
[customViewController setFunky:YES];
The other option is to create a protocol for a MyCustomViewDelegate. If you're not familiar with protocols, your header would look like this:
#class MyCustomViewController;
#protocol MyCustomViewDelegate
#required
-(BOOL)customViewShouldBeFunky:(MyCustomViewController*)customView;
#end
#interface MyCustomViewController : UIViewController {
NSObject<MyCustomViewDelegate> *delegate;
}
#property (readwrite, retain) IBOutlet NSObject<MyCustomViewDelegate> *delegate;
#end
Since it is an IBOutlet, you can wire up the delegate like any other delegate in Interface Builder.
Then call [delegate customViewShouldBeFunky:self] when you need to determine how your view should behave.
Have two subclasses is probably easier, and will be easier to document.
Here is an example of overriding properties and setting them in custom classes, this may help. The property code will work before awakeFromNib is called. So you may decide what you have to do based on the user's decision right in awakeFromNib.
https://stackoverflow.com/a/31094561/1699210