I'm trying to add a new sub view form a nib using swift for OS X.
So far i've:
created a new "Cocoa Application"
added a new "Cocoa Class" called "TestSubView" as a subclass of NSViewController with a XIB file
I want to add this subview to my main view when the application loads.
in my ViewController ( the ViewController for the main window ) i have.
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let newSubView = TestSubView();
self.view.addSubview(newSubView.view);
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
But i'm getting the following error
Failed to set (contentViewController) user defined inspected property on (NSWindow):
-[NSNib initWithNibNamed:bundle:] could not load the nibName: temp.TestSubView in bundle (null).
I realise i will need to size and position this subview but I can't seem to get to that point.
I've spent the better part of a day trying to figure this one out so any help would be greatly appreciated.
I finally got this thing to work. My new code looks like
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let subview = TestSubView(nibName: "TestSubView", bundle: nil)!
self.view.addSubview(subview.view)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Found with the help of the docs & this answer
It was suggested that if the nib name and the class name are the same you shouldn't need to declare nibname: (as i'd tried to do originally) but the docs didn't mention this - explains why it didn't work!
For prosperity, this worked for me with Xcode 6.1.1 on OS X Yosemite (10.10.1)
A nib is really nothing but an XML file with view information in it. You have to get it from the application bundle and get one of the views contained in it explicitly. You are perhaps confounding views and view controllers (your attempt to extract view from newSubView suggests that).
Try this:
let subview = NSBundle.mainBundle().loadNibNamed("TestSubView",
owner:self, options:nil)![0]! // maybe no final unwrapping "!" in Swift 3
self.view.addSubview(subview)
Make sure the xib is really called the name you are using and contains at a least one view (otherwise the two unwrapping ! above will crash your app).
Related
I'm trying to find how to bring up a second view/window after pushing a button on my primary window. I have read about segues and I can get the first window to display the second but the second is not connected to a view controller so I can't add any code to any controls on the second view. Try as I might I cannot create a SecondViewController.swift file and connect it to a window controller or a view controller. The tutorials I have found all deal with iOS and I want OS X which means there are just enough differences to keep me from figuring this out.
Can anyone show me how to do this?
Ta,
A.
First make new file like:
After that, put these codes in your classes and that should do it.
class SecondWindowController: NSWindowController {
convenience init() {
self.init(windowNibName: "SecondWindowController")
}
}
class ViewController: NSViewController {
private var secondWindowController: SecondWindowController?
#IBAction func showSecondWindow(sender: AnyObject) {
if secondWindowController == nil {
secondWindowController = SecondWindowController()
}
secondWindowController?.showWindow(self)
}
}
PROBLEM: Getting a Thread 1: EXC_BAD_ACCESS (code=1, address=0x18) when setting the previewItem of a QLPreviewView object laid out in a window.
GOAL: Create an app with a preview of file contents and controls below for changing properties of the file being previewed. The preview must be tied to the size of the window and react to resizing of the window.
SETUP:
Using Xcode 6.1.1 I created a default OS X Cocoa Application using Swift, and Storyboards.
Using Storyboards for layout, there doesn't appear to be a QLPreviewView component in the Object Library, so I used a Custom View object and set the Class to QLPreviewView. I then set the constraints to allow the preview view to resize with the window frame.
I then linked the Custom View of class QLPreviewView into the ViewController.swift file as a member variable. In the viewDidLoad() method I added a set the previewItem of the QLPreviewView object to an implementation of the QLPreviewItem protocol. I also added an import Quartz.
Here is the source for ViewController.swift, as generated by Xcode, then modified by me to add the setup for QLPreviewView.
//
// ViewController.swift
// PreviewTest
//
// Created by Derek on 2015-03-02.
// Copyright (c) 2015 Derek. All rights reserved.
//
import Cocoa
import Quartz
class ViewController: NSViewController {
#IBOutlet var preview: QLPreviewView!
override func viewDidLoad() {
super.viewDidLoad()
preview.previewItem = {final class PreviewItem : NSObject, QLPreviewItem {
var previewItemURL: NSURL! {return NSURL(fileURLWithPath: "/Users/derek/Pictures/Scan.jpeg")}
var previewItemTitle: String! {return "Test" }
}
return PreviewItem()
} ()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
The line where preview.previewItem is set gets the EXC_BAD_ACCESS error described at the top when the application is run.
I have successfully created a QLPreviewView and embedded it in a window, however I did it without using the storyboards or constraints, so it's size and position is not managed by the window, which is what I want.
The inline class PreviewItem works fine. I have tried switching it to a standalone class and then created it to a variable, then set QLPreviewView.previewItem to the variable. This fails in the same way as the set to previewItem doing it inline.
The inline class PreviewItem also works fine in the non storyboard case and the file scan.jpeg is shown as expected.
Other UI objects, like Text Field work fine when I set them up in a similar way.
I'm hoping to find out what I'm doing wrong here and how to correct it. Or suggestions on how to approach this goal in a different way.
Try defining PreviewItem as a nested class instead of the inline definition. JavaScript might define and invoke it with () like your program, but I think in Swift the nested class is more general.
class ViewController: NSViewController {
private final class PreviewItem : NSObject, QLPreviewItem {
var previewItemURL: NSURL! {return NSURL(fileURLWithPath: "/Users/derek/Pictures/Scan.jpeg")}
var previewItemTitle: String! {return "Test" }
}
#IBOutlet var preview: QLPreviewView!
override func viewDidLoad() {
super.viewDidLoad()
preview.previewItem = PreviewItem()
self.view.addSubview(preview)
// Do any additional setup after loading the view.
}
...
}
Also make sure the preview property is connected to the view by Interface Builder.
I've recently started working on some test projects to get the feel for OS X development with Xcode. I come from Windows, so I might not be making much sense here.
How would I subscribe to certain "events" in Swift? I have just learned how to connect actions to UI objects. For example, I can now click a button, and change the text of a label programatically. However, and this may just be a case of lack of knowledge on my part - I am not able to find a way to subscribe to a TextField's "Text Changed" event.
Let's say that I have a TextField, and when I change the text at runtime (i.e. type something), I want to do something in the textChanged event for that particular TextField.
Is there even such a thing as a TextChanged event in OS X development?
Update
I am now using the following code:
import Cocoa
class ViewController: NSViewController {
class textField:NSTextField, NSTextFieldDelegate
{
override func awakeFromNib() {
delegate = self;
}
override func controlTextDidChange(obj: NSNotification)
{
println("Text changed.")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
And I have added a ClassName to the TextField control in the Identity Inspector, but it isn't responding to the text changing. The message given is:
Failed to connect (textField) outlet from
(Xcode_Action_Basics.ViewController) to (NSTextField): missing setter
or instance variable
I just googled that error and came across this page: Failed to connect (storyboard) outlet from (NSApplication) to (NSNibExternalObjectPlaceholder) error in Cocoa and storyboard which states that this is a known issue in Xcode and that it does not mean there is a problem with your code - but I'm not so sure about that, because the code isn't working. Not sure if I've missed out on something.
Create a class that implements the protocol NSTextFieldDelegate like
class MyTextField:NSTextField, NSTextFieldDelegate {
override func awakeFromNib() {
delegate = self // tell that we care for ourselfs
}
override func controlTextDidChange(obj: NSNotification) {
// .... handle change, there are a lot of other similar methods. See help
}
}
In IB assign this class here:
I try to make simple OSX application with WebView. I am new in Swift, and have some errors:
it's my WebView delegate file:
class WebViewControllerDelegate: NSObject{
#IBOutlet var webview: WebView!
override init()
{
super.init()
self.webview.frameLoadDelegate = self
let url = NSURL(string: "http://google.com")
let request = NSURLRequest(URL: url);
self.webview.mainFrame.loadRequest(request)
}
func didFinishLoadForFrame()
{
println("ok:")
}
}
I try to run this, but have EXC_BAD_INSTRUCTION error at line where I set frameLoadDelegate to self. I think it's error with web view outlet, but i can't fix it.
self.webview is nil, and i don't know why...
Views in Xib / Storyboard files are not loaded nor connected during initialization. That is why self.webview is nil in your code.
You really should not attach the IBOutlet directly to your "delegate". Instead, connect it to your view controller and have the view controller set it on the delegate. You should do that in viewDidLoad because that is when you can guarantee all of the objects have been created and connected.
I had same error before. I give you a suggestion for solve this error. Do following things...
1. Click on the View Controller open the connection inspector.
See this...
2. And then you have to double check on the connection inspector window and find something contain like following image...
3. Then you have to delete one outlet. According to my one I have to delete below one... finally solved one...
Then run the code...
I am new to IOS developing and recently started in Xcode 4.5. I saw for every viewController that i could set some identity variables including the storyboard ID. What is this, and how can I use it?
I started searching on stackoverflow and couldn't find any explanation for it.
I assumed it's not just some stupid label that I can set to remember my controller right? What does it do?
The storyboard ID is a String field that you can use to create a new ViewController based on that storyboard ViewController. An example use would be from any ViewController:
//Maybe make a button that when clicked calls this method
- (IBAction)buttonPressed:(id)sender
{
MyCustomViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
This will create a MyCustomViewController based on the storyboard ViewController you named "MyViewController" and present it above your current View Controller
And if you are in your app delegate you could use
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle: nil];
Edit: Swift
#IBAction func buttonPressed(sender: AnyObject) {
let vc = storyboard?.instantiateViewControllerWithIdentifier("MyViewController") as MyCustomViewController
presentViewController(vc, animated: true, completion: nil)
}
Edit for Swift >= 3:
#IBAction func buttonPressed(sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "MyViewController") as! ViewController
present(vc, animated: true, completion: nil)
}
and
let storyboard = UIStoryboard(name: "MainStoryboard", bundle: nil)
To add to Eric's answer and update it for Xcode 8 and Swift 3:
A storyboard ID does exactly what the name implies: it identifies. Just that it identifies a view controller in a storyboard file. It is how the storyboard knows which view controller is which.
Now, don't be confused by the name. A storyboard ID doesn't identify a 'storyboard'. A storyboard, according to Apple's documentation, 'represents the view controllers for all or part of your app’s user interface.' So, when you have something like the picture below, you have a storyboard called Main.storyboard which has two view controllers, each of which could be given a storyboard ID (their ID in the storyboard).
You can use a view controller's storyboard ID to instantiate and return that view controller. You can then go ahead to manipulate and present it however you want. To use Eric's example, say you want to present a view controller with identifier 'MyViewController' when a button is pressed, you would do it this way:
#IBAction func buttonPressed(sender: Any) {
// Here is where we create an instance of our view controller. instantiateViewController(withIdentifier:) will create an instance of the view controller every time it is called. That means you could create another instance when another button is pressed, for example.
let vc = storyboard?.instantiateViewController(withIdentifier: "MyViewController") as! ViewController
present(vc, animated: true, completion: nil)
}
Please take note of changes in syntax.