UICollectionView doesn't contain UICollectionViewCell in IB - xcode

I'm trying to add a UICollectionView to a .nib file in IB. I've worked through a few tutorials and had no problems.
In my existing app, when I drag a collection view into a view in IB and then expand the object, the CollectionView Flow Layout is there , but there is no collection view cell. I also cannot drag and drop a cell into the collection view. Also the collection view is displayed differently, showing an image of a grid of cells instead of the normal white box.
I've tried creating a new view controller with nib, and have the same results when adding a collectionView.
This app WAS targeting iOS 4.2, but I've changed the deployment target to iOS 6.
In any newly created projects I don't get this behavior.

I can't tell you why it does not work, but for me the subclass approach from this blog post solved the problem:
http://www.adoptioncurve.net/archives/2012/09/a-simple-uicollectionview-tutorial.php
here is a short summary:
create a new class MyViewCell which extends UICollectionViewCell and create properties, actions, outlets and methods as needed.
#interface MyViewCell : UICollectionViewCell
create a View (xib file) with a Collection View Cell as its root object (and delete the object which is created by default).
in the attributes set the custom class of this Collection View Cell to your extended class MyViewCell (instead of the default UICollectionViewCell).
in the attributes under Collection Reusable View define an Identifier, e.g. myCell, you will need this later to register your call.
go back to your custom class and modify the inithWithFrame method to load and use your created view.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:#"CVCell" owner:self options:nil];
if ([arrayOfViews count] < 1) { return nil; }
if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]]) { return nil; }
self = [arrayOfViews objectAtIndex:0];
}
return self;
}
then you can register this class to be used by the collectionView with
[self.collectionView registerClass:[MyViewCell class] forCellWithReuseIdentifier:#"myCell"];

It's been a while since the question has been asked but I faced the same issue lately.
Eventually, I found the answer here.
In short:
You cannot drag and drop a UICollectionViewCell into a UICollectionView if you are in a .xib file. This is only possible in a storyboard.
The workaround is to create a .xib file for your custom cell and to register the cell manually using:
[self.collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:CELL_ID];

Define your cell view in a separate nib file. The view must be of type UICollectionViewCell or a subclass.
Then, you must register the nib with your collection view:
- (void)viewDidLoad
{
UINib *cellNib = [UINib nibWithNibName:#"MyNib" bundle:nil];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:#"cell"];
}

While these answers are correct, the underlying cause of the problem is quite subtle and Apple needs to update their error message in the log. When you see:
Unknown class MyViewCell in Interface Builder file.
What it actually means is that your source code is missing the #implementation declaration or that the .m file has not been added to the target. So check your code and make sure you have:
#implementation MyViewCell
#end
This is true for both UITableViewCell and UICollectionViewCell.
I discuss the problem further at https://stackoverflow.com/a/12755891/539149 for the general case.
The interesting thing is that if you are missing the #implementation and call:
[self.myCollectionView registerClass:[MyViewCell class] forCellWithReuseIdentifier:#"MyViewCell"];
You will see:
Undefined symbols for architecture armv7:
"_OBJC_CLASS_$_MyViewCell", referenced from:
objc-class-ref in MyViewController.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
So Apple knows about the problem, they just aren't detecting it during compilation. I would advise against putting custom cells in separate nibs or calling registerClass: forCellWithReuseIdentifier:. It's much cleaner to store the custom cell inside of its container UICollectionView or UITableView in Interface Builder.

Related

How to use NSViewController in an NSDocument-based Cocoa app

I've got plenty of experience with iOS, but Cocoa has me a bit confused. I read through several Apple docs on Cocoa but there are still details that I could not find anywhere. It seems the documentation was written before the NSDocument-based Xcode template was updated to use NSViewController, so I am not clear on how exactly I should organize my application. The template creates a storyboard with an NSWindow, NSViewController.
My understanding is that I should probably subclass NSWindowController or NSWindow to have a reference to my model object, and set that in makeWindowControllers(). But if I'd like to make use of the NSViewController instead of just putting everything in the window, I would also need to access my model there somehow too. I notice there is something called a representedObject in my view controller which seems like it's meant to hold some model object (to then be cast), but it's always nil. How does this get set?
I'm finding it hard to properly formulate this question, but I guess what I'm asking is:how do I properly use NSViewController in my document-based application?
PS: I understand that NSWindowController is generally meant to managing multiple windows that act on one document, so presumably if I only need one window then I don't need an NSWindowController. However, requirements might change and having using NSWindowController may be better in the long run, right?
I haven't dived into storyboards but here is how it works:
If your app has to support 10.9 and lower create custom of subclass NSWindowController
Put code like this into NSDocument subclass
- (void)makeWindowControllers
{
CustomWindowController *controller = [[CustomWindowController alloc] init];
[self addWindowController:controller];
}
If your app has multiple windows than add them here or somewhere else (loaded on demand) but do not forget to add it to array of document windowscontroller (addWindowController:)
If you create them but you don't want to show all the windows then override
- (void)showWindows
{
[controller showWindow:nil]
}
You can anytime access you model in your window controller
- (CustomDocument *)document
{
return [self document];
}
Use bindings in your window controller (windowcontroller subclass + document in the keypath which is a property of window controller)
[self.textView bind:#"editable"
toObject:self withKeyPath:#"document.readOnly"
options:#{NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName}];
In contrast to iOS most of the views are on screen so you have to rely on patterns: Delegation, Notification, Events (responder chain) and of course MVC.
10.10 Yosemite Changes:
NSViewController starting from 10.10 is automatically added to responder chain (generally target of the action is unknown | NSApp sendAction:to:from:)
and all the delegates such as viewDidLoad... familiar from iOS are finally implemented. This means that I don't see big benefit of subclassing NSWindowCotroller anymore.
NSDocument subclass is mandatory and NSViewController is sufficient.
You can anytime access you data in your view controller
- (CustomDocument *)document
{
return (CustomDocument *)[[NSDocumentController sharedDocumentController] documentForWindow:[[self view] window]];
//doesn't work if you do template approach
//NSWindowController *controller = [[[self view] window] windowController];
//CustomDocument *document = [controller document];
}
If you do like this (conforming to KVC/KVO) you can do binding as written above.
Tips:
Correctly implement UNDO for your model objects in Document e.g. or shamefully call updateChangeCount:
[[self.undoManager prepareWithInvocationTarget:self] deleteRowsAtIndexes:insertedIndexes];
Do not put code related to views/windows into your Document
Split your app into multiple NSViewControllers e.g.
- (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:AAPLListWindowControllerShowAddItemViewControllerSegueIdentifier]) {
AAPLListViewController *listViewController = (AAPLListViewController *)self.window.contentViewController;
AAPLAddItemViewController *addItemViewController = segue.destinationController;
addItemViewController.delegate = listViewController;
}
}
Previous code is called on windowcontroller with viewcontroller as delegate (again possible only after 10.10)
I always prefer to use multiple XIBs rather than one giant storyboard/XIB. Use following subclass of NSViewController and always inherit from it:
#import <Cocoa/Cocoa.h>
#interface MyViewController : NSViewController
#property(strong) IBOutlet NSView *viewToSubstitute;
#end
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
- (void)awakeFromNib
{
NSView *view = [self viewToSubstitute];
if (view) {
[self setViewToSubstitute:nil];
[[self view] setFrame:[view frame]];
[[self view] setAutoresizingMask:[view autoresizingMask]];
[[view superview] replaceSubview:view with:[self view]];
}
}
#end
Add a subclass of MyViewController to the project with XIB. Rename the XIB
Add NSViewController Object to the XIB and change its subclass name
Change the loading XIB name to name from step 1
Link view to substitute to the view you want to replace
Check example project Example Multi XIB project
Inspire yourself by shapeart or lister or TextEdit
And a real guide is to use Hopper and see how other apps are done.
PS: You can add your views/viewcontroller into responder chain manually.
PS2: If you are beginner don't over-architect. Be happy with the fact that your app works.
I'm relatively new to this myself but hopefully I can add a little insight.
You can use the view controllers much as you would in ios. You can set outlets and targets and such. For NSDocument-based apps you can use a view controller or the window controller but I think for most applications you'll end up using both with most of the logic being in the view controller. Put the logic wherever it makes the most sense. For example, if your nsdocument can have multiple window types then use the view controller for logic specific to each type and the window controller for logic that applies to all the types.
The representedObject property is primarily associated with Cocoa bindings. While I am beginning to become familiar with bindings I don't have enough background to go into detail here. But a search through the bindings programming guide might be helpful. In general bindings can take the place of a lot of data source code you would need to write on ios. When it works it's magical. When it doesn't work it's like debugging magic. It can be a challenge to see where things went wrong.
Let me add a simple copy-pastable sample for the short answer category;
In your NSDocument subclass, send self to the represented object of your view controller when you are called to makeWindowControllers:
- (void) makeWindowControllers
{
NSStoryboard* storyboard = [NSStoryboard storyboardWithName: #"My Story Board" bundle: nil];
NSWindowController* windowController = [storyboard instantiateControllerWithIdentifier: #"My Document Window Controller"];
MyViewController* myController = (id) windowController.contentViewController;
[self addWindowController: windowController];
myController.representedObject = self;
}
In you MyViewController subclass of NSViewController, overwrite setRepresentedObject to trap it's value, send it to super and then make a call to refresh your view:
- (void) setRepresentedObject: (id) representedObject
{
super.representedObject = representedObject;
[self myUpdateWindowUIFromContent];
}
Merci, bonsoir, you're done.

WebView - Failed to set (contentViewController) user defined inspected property on (NSWindow)

I create a new Xcode project from scratch and it compiles fine.
I add a label to the View Controller and this compiles / runs / shows fine.
I then drag in a WebView into the View Controller but get this message when I run the application:
Failed to set (contentViewController) user defined inspected property on (NSWindow): *** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (WebView)
What am I doing wrong?
Just Link Webkit.framework in general->Linked Framework and Libraries
Well first of all you need to define the Web view so it actually knows what todo all it is doing right now is trying to find what to run here, see a tutorial on youtube or such, this is the code i used for mine, just to set it up.
first you want to put the web view into the view controller and add a toolbar to the bottom of it.
add four bar button items onto the bar.
and put a fixed space in-between the two.
then you need to set the styles of them do this by going to attributes inspector> button type > choose style
choose the following for the button 1st one is rewind 2nd is forward 3rd one is refresh last one is Cancel.
then control click and drag the button to the web view it will give you the options for it like load rewind and refresh.
then go to your storyboard and make sure you have you main and header files connected do this by selected the view controller in the storyboard files, then
change the class to the view controller you want to hook up to.
then hit enter
go to your header file(.h) and insert the following code,
#property (nonatomic, strong) IBOutlet UIWebView *webView;
put it under the #interface with brackets but make sure that the code is not inside of the brackets.
then what you need to do is go to your main file(.m) and insert the following code like this
#interface WebViewController ()
#end
#implementation WebViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//custom initialization
}
return self;
}
- (void)viewDidLoad {
NSURL *url = [NSURL URLWithString:#"https://www.google.com"];
NSURLRequest *requestURL = [NSURLRequest requestWithURL:url]; [_webView loadRequest:requestURL];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
accept obviously you need to replace the void or just past into the right places
Then build and Run your code use iOS simulator iPhone 6.
Hope this helps have a nice day!
You should be using the wrong property webFrame of WebView instead of mainFrame
if let url = NSBundle.mainBundle().URLForResource("index", withExtension: "html"){
editorWebView.mainFrame.loadRequest(NSURLRequest(URL: url))
}
I don't think you are doing anything wrong. Its just a bug

Objective C - XIB load in NSView

I'm new to Objective C coding and the MVC concept gives me some pain. My goal is to develop a Mac Application. I was able to create a .xib for the main window. It starts well.
My main NSWindow has its main NSView. In this NSView, I put 3 subwiews (using Interface Builder). What I want is to change the content of the 2nd subview (by loading the content from another XIB file, called SubWindow1.xib) when I click on a button placed in the 1st subview.
I'm able to catch the click and tried some "xib loading" code but it doesn't work (the content never appears in my 2nd subview).
What I did is:
put the NSButton (on the 1st subview)
put a NSObject on Interface Builder (class renamed CtMainWindow, "Ct" for Controler)
link the IBAction (changeToSubWindow1) and IBOutlet (vSubView2 which is my 2nd subview)
create the CtMainWindow class, create a VwSubWindow1 class (which extends NSView)
In changeToSubWindow1(), I wrote:
VwSubWindow1 * vProfile = [[VwSubWindow1 alloc] initWithFrame:vSubView2.bounds];
[vProfile loadXib];
[vProfile.superview addSubview:vProfile];
The loadXib() function does:
NSNib * nib = [[NSNib alloc] initWithNibNamed:[self #"SubWindow1"] bundle:nil]; //#"SubWindow1" is the name of the expected loaded xib without .xib extention
[nib instantiateWithOwner:self topLevelObjects:nil];
And Voila. I was hoping that loading the sub xib in a view, and putting that view in my 2nd subview, it would appear.
I also tried to put only the following code in changeToSubWindow1()
VwSubWindow1 * vProfile = [[VwSubWindow1 alloc] initWithFrame:vSubView2.bounds];
[vProfile loadSubWindow];
The loadSubWindow() function does:
[NSBundle loadNibNamed:#"SubWindow1" owner:self];
Without more success.
I already read lots of threads from stackoverflow and tried lots of solutions but none of them worked for me.
Can anyone give me the hint I need in order to load and display the Xib in a subview ?
Thanks in advance.
you can try this method
you can put 3 xib files and 3 windows. connect each window
self.packages = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.window1.contentView addSubview:self.vc.view];
self.packages.view.frame = ((NSView*)self.window1.contentView).bounds;
[window1 makeKeyAndOrderFront:NSApp];

NSWindowController object linkage in Interface builder

I created a NSWindow xib file that I want to open on click of a button in another window.
Now, to control the behavior of the NSWindow, I dragged an object from Library in xib and changed it to subclass of NSWindowController (i.e. ListingWindowController) that I defined in XCode.
Similarly I also created a subclass of NSViewController (i.e. ListingViewController) to manage the NSView inside the NSWindow. To do this, I dragged NSViewController from Library in xib and changed its class to ListingViewController.
#class ListingViewController;
#interface ListingWindowController : NSWindowController {
IBOutlet ListingViewController *listingVC;
}
#property (nonatomic, retain) IBOutlet ListingViewController *listingVC;
#end
I connected window and listingVC of my window controller in IB.
Now to invoke this window on click of a button in my launch (first) window, I create the window controller using initWithWindowNibName like this..
- (IBAction) pushConnect:(id)sender {
NSLog(#"Connect pushed.");
if (wc == nil) {
wc = [[ListingWindowController alloc] initWithWindowNibName:#"ListingWindow" owner:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(closeWindow:) name:NSWindowWillCloseNotification object:nil];
[wc showWindow:sender];
}
}
The problem is that despite all the bindings done in IB for the view controllers of upcoming window/view, the window and listingVC comes out to be (null), (null) even after the new window has loaded (below code).
- (void)windowDidLoad {
[super windowDidLoad];
NSLog(#"windowDidLoad = %#, %#", self.window, self.listingVC);
}
Please help why the connections are not working. I'm banging my head against this problem for quite a while now.
PS: I'm coming from iOS programming background. So, I'm assuming the Mac's window/view controller behave similar to iOS UIViewControllers.
TIA..
Note that:
wc = [[ListingWindowController alloc] initWithWindowNibName:#"ListingWindow" owner:self];
means that self (it’s not clear what self is from your question) is the owner of ListingWindow.nib. This means that self is the one who keeps outlets to objects in that nib file, and self is responsible for releasing the top-level objects in the nib file. This also means that you’re creating an instance of ListingWindowController in your code and another instance inside your nib file since you’ve dragged an object of class ListingWindowController onto the nib file.
This is not how it’s supposed to be.
In the vast majority of cases, a window (view) controller loads a nib file and becomes its owner. It has a window (view) outlet that must be linked to a top-level window (view) in the nib file. Being the nib file’s owner, it must have been created before the nib file is loaded.
In order to achieve this for your window controller, you need to set the file’s owner class to ListingWindowController. You must not drag an object cube and instantiate the window controller inside the nib file. The window controller is the owner of the nib file, so it must exist before the nib file is loaded. You must also link the window outlet in file’s owner to the top-level window object in the nib file so that the window controller is aware of what window it should manage.
Having done that, use:
wc = [[ListingWindowController alloc] initWithWindowNibName:#"ListingWindow"];
instead of:
wc = [[ListingWindowController alloc] initWithWindowNibName:#"ListingWindow" owner:self];
since wc is supposed to be the owner of the nib file.
View controllers work similarly. They’re created before loading the nib file, are responsible for loading a nib file that contains a view as a top-level object, are that nib file’s owner, and have a view outlet that must be linked to that top-level view.
It’s not clear from your question whether you have a separate nib file for the view. If you don’t, then using a subclass of NSViewController is not needed at all — you could use a subclass of NSObject instead. If you insist on using NSViewController to manage a view that’s not loaded from a separate nib file, then you should override -loadView so that you get a reference to the view by some means other than loading it from a nib file, and sending it -setView: so that it is aware of the view it’s supposed to be managing.
Recommended reading: Nib Files in the Resource Programming Guide, NSWindowController class reference, NSViewController class reference.

NSViewController and multiple subviews from a Nib

I'm having a difficult time wrapping my head around loading views with Interface Builder and NSViewController.
My goal is to have a view which meets the following description: Top bar at the top (like a toolbar but not exactly) which spans the entire width of the view, and a second "content view" below. This composite view is owned by my NSViewController subclass.
It made sense to use Interface Builder for this. I have created a view nib, and added to it two subviews, laid them out properly (with the top bar and the content view). I've set File's Owner to be MyViewController, and connected outlets and such.
The views I wish to load in (the bar and the content) are also in their own nibs (this might be what's tripping me up) and those nibs have their Custom Class set to the respective NSView subclass where applicable. I'm not sure what to set as their File's Owner (I'm guessing MyController as it should be their owner).
Alas, when I init an instance of MyViewController none of my nibs actually display. I've added it to my Window's contentView properly (I've checked otherwise), and actually, things sort of load. That is, awakeFromNib gets sent to the bar view, but it does not display in the window. I think I've definitely got some wires crossed somewhere. Perhaps someone could lend a hand to relieve some of my frustration?
EDIT some code to show what I'm doing
The controller is loaded when my application finishes launching, from the app delegate:
MyController *controller = [[MyController alloc] initWithNibName:#"MyController" bundle:nil];
[window setContentView:[controller view]];
And then in my initWithNibName I don't do anything but call to super for now.
When breaking out each view into its own nib and using NSViewController, the typical way of handling things is to create an NSViewController subclass for each of your nibs. The File's Owner for each respective nib file would then be set to that NSViewController subclass, and you would hook up the view outlet to your custom view in the nib. Then, in the view controller that controls the main window content view, you instantiate an instance of each NSViewController subclass, then add that controller's view to your window.
A quick bit of code - in this code, I'm calling the main content view controller MainViewController, the controller for the "toolbar" is TopViewController, and the rest of the content is ContentViewController
//MainViewController.h
#interface MainViewController : NSViewController
{
//These would just be custom views included in the main nib file that serve
//as placeholders for where to insert the views coming from other nibs
IBOutlet NSView* topView;
IBOutlet NSView* contentView;
TopViewController* topViewController;
ContentViewController* contentViewController;
}
#end
//MainViewController.m
#implementation MainViewController
//loadView is declared in NSViewController, but awakeFromNib would work also
//this is preferred to doing things in initWithNibName:bundle: because
//views are loaded lazily, so you don't need to go loading the other nibs
//until your own nib has actually been loaded.
- (void)loadView
{
[super loadView];
topViewController = [[TopViewController alloc] initWithNibName:#"TopView" bundle:nil];
[[topViewController view] setFrame:[topView frame]];
[[self view] replaceSubview:topView with:[topViewController view]];
contentViewController = [[ContentViewController alloc] initWithNibName:#"ContentView" bundle:nil];
[[contentViewController view] setFrame:[contentView frame]];
[[self view] replaceSubview:contentView with:[contentViewController view]];
}
#end
Should not MainViewController be a subclass of NSWindowController? And the outlets in the class connected to view elements in the main Window in MainMenu.xib?
Let's hope old threads are still read...

Resources