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
Related
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.
In several parts of my application, I need a control where a user can enter a password. The password field is secure, however I want the user to have the ability to switch the field to plain text and check for typos, etc.
The password field itself is no problem. I subclassed NSSecureTextField to get the behaviour I want. I make it bindable, so I can toggle the display from another control (say, a Checkbox).
Now, I want to reuse this in multiple places within my application. I can simply use my custom secure text field along side a checkbox everywhere I need this. But I would prefer to group these pieces together into a reusable component.
So I create one using interface builder:
Now, how can I use that in my different windows? I have added a 'Custom View' component to my window, and set it's type appropriately:
But at runtime, I just get an empty space.
1 - Presumably because my view is defined in a .xib - I need to do something in code to load my PasswordView instance from the .xib; but what exactly do I need to do? I'm having trouble working this out.
Specifically:
What is the process / API to load an instance of my view from a .xib? I'm struggling to find the documentation on this.
Once I have loaded an instance of my view, how to insert it in place of my Custom View 'placeholder'? Presumably I need an outlet to my custom view, but then what? Do I just assign my loaded instance to the outlet? Am I supposed to add it as a subview to the outlet?
Where should this logic be happening? Inside init, inside AwakeFromNib?
2 - I know I can use an NSViewController for my custom view (example), and load it that way. But do I really need a view controller just for this? It seems I should just be able to create standalone views and instantiate them easily... But perhaps this is not the Cocoa 'way'? It seems to me that there should be a straightforward way of composing a view from multiple .xibs without needing a controller for every sub view (which may just be a text field, or a button), but I'm just not finding it...
Thank you in advance.
If I am getting from your question - You have an XIB having window that contains a passwordTextField and a checkBox.
And you want this xib window to load everytime you need to show password field.
I would suggest you not to create a window, you can create an NSView with a passwordTextField and a checkBox.
And in all the windows draw an empty view and you can load this xib view onto that, so a single object can be used multiple times.
EDIT:
Here is some sample code how I use to load a labelView on main Window.
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize myLabelViewController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
myLabelViewController=[[LabelView alloc] initWithNibName:#"LabelView" bundle:nil];
[_window setContentView:[myLabelViewController view]];
}
#end
LabelView.h
#import <Cocoa/Cocoa.h>
#interface LabelView : NSViewController
#end
LabelView.m
#import "LabelView.h"
#implementation LabelView
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
}
return self;
}
#end
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];
I am trying to get the current URL and title from the WebView. I've used this for the URL
- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
// Only report feedback for the main frame.
if (frame == [sender mainFrame]){
NSString *url = [[[[frame provisionalDataSource] request] URL] absoluteString];
[addressBar setStringValue:url];
}
}
and this for the title:
- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame
{
// Report feedback only for the main frame.
if (frame == [sender mainFrame]){
[[sender window] setTitle:title];
}
}
This code comes straight from Apple's WebKit Objective-C Programming Guide. I only slightly modified the URL method to the addressBar instead of textField. But it doesn't work. The addressBar field is never populated with the URL of the page and the window title doesn't update ether. Everything is connected correctly in interface builder. Why won't it work?
If the page has already loaded then it is not a provisionaldatasource, replace this with
[[[[frame dataSource] request] URL] absoluteString];
Unfortunately, I myself don't know. But what I have found is a great website with tutorials one of which is to make an RSS feed and in that it takes the title from the RSS. You'll see it if your scroll down. Hope you can modify it to work!!
http://www.raywenderlich.com/2636/how-to-make-a-simple-rss-reader-iphone-app-tutorial
Here's you you get this working.
Set up your frameLoadDelegate for the WebView object. In your visual interface, control-drag from your WebView element to your NSWindow that is containing the WebView. When you release the mouse, a small black drop-down menu will appear. Select frameLoadDelegate. Once you've done this, messages such as webView:DidReceiveTitle:forFrame will be sent to the instance of your NSWindow.
Create a Subclass out of NSWindow and assign your NSWindow object to this subclass. Since this new child object will inherit everything from NSWindow, it will receive the webView:DidReceiveTitle:forFrame message.
Paste in your code above into this new child class. This effectively overrides the method definitions from the parent class, and gives you autonomous control over what happens.
Hope that helps.
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.