jiminybob99
jiminybob99

Reputation: 873

Detailed instruction on use of NSOpenPanel

I want to be able to open an image in Swift. This is my first Swift project.

@IBAction func SelectFileToOpen(sender: NSMenuItem) {
    var openPanel = NSOpenPanel();
    openPanel.allowsMultipleSelection = false;
    openPanel.canChooseDirectories = false;
    openPanel.canCreateDirectories = false;
    openPanel.canChooseFiles = true;
    let i = openPanel.runModal();
    if(i == NSOKButton){
        print(openPanel.URL);
        var lettersPic = NSImage(contentsOfURL: openPanel.URL!);
        imageView.image = lettersPic;

    }
}

Output of my NSLog when using the open panel

Optional(file:///Users/ethansanford/Desktop/BigWriting.png)
fatal error: unexpectedly found nil while unwrapping an Optional value

How can I allow the user to open a png file of interest. When I specifying the same file in the code everything works well. An example of me indicating which file to open in the code without using the open file panel and acting as a user:

let pictureURl = NSURL(fileURLWithPath: "///Users/ethansanford/Desktop/BigWriting.png");
var lettersPic = NSImage(contentsOfURL: pictureURl!);
imageView.image = lettersPic; 

Is there a problem with the format of my URL or something? Any help would be appreciated.

Upvotes: 13

Views: 15846

Answers (3)

Leo Dabus
Leo Dabus

Reputation: 236478

Add a new file to your project (swift source file) and add this extension there

Xcode 9 • Swift 4

extension NSOpenPanel {
    var selectUrl: URL? {
        title = "Select Image"
        allowsMultipleSelection = false
        canChooseDirectories = false
        canChooseFiles = true
        canCreateDirectories = false
        allowedFileTypes = ["jpg","png","pdf","pct", "bmp", "tiff"]  // to allow only images, just comment out this line to allow any file type to be selected 
        return runModal() == .OK ? urls.first : nil
    }
    var selectUrls: [URL]? {
        title = "Select Images"
        allowsMultipleSelection = true
        canChooseDirectories = false
        canChooseFiles = true
        canCreateDirectories = false
        allowedFileTypes = ["jpg","png","pdf","pct", "bmp", "tiff"]  // to allow only images, just comment out this line to allow any file type to be selected
        return runModal() == .OK ? urls : nil
    }
}

In your View Controller:

class ViewController: NSViewController {
    @IBOutlet weak var imageView: NSImageView!
    @IBAction func saveDocument(_ sender: NSMenuItem) {
        print("SAVE")
    }
    @IBAction func newDocument(_ sender: NSMenuItem) {
        print("NEW")
    }
    // connect your view controller to the first responder window adding the openDocument method
    @IBAction func openDocument(_ sender: NSMenuItem) {
        print("openDocument ViewController")
        if let url = NSOpenPanel().selectUrl {
            imageView.image = NSImage(contentsOf: url)
            print("file selected:", url.path)
        } else {
            print("file selection was canceled")
        }
    }
}

Upvotes: 21

jwlaughton
jwlaughton

Reputation: 925

Hmmm... I didn't see anything wrong necessarily with your code, so I test ran this code (selecting a PNG file on my desktop):

let openPanel = NSOpenPanel()
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = true
let i = openPanel.runModal()
if(i == NSModalResponseOK){
    print(openPanel.URL)
    let lettersPic = NSImage(contentsOfURL: openPanel.URL!)
    print(lettersPic)      
}

What I got was:

Optional(file:///Users/jwlaughton/Desktop/flame%2012-32.png)
Optional( "NSBitmapImageRep 0x6000000a4140 Size={1440, 900} ColorSpace=(not yet loaded) BPS=8 BPP=(not yet loaded) Pixels=1440x900 Alpha=NO Planar=NO Format=(not yet loaded) CurrentBacking=nil (faulting) CGImageSource=0x608000160cc0" )>)

Which seems OK to me.

Maybe the issue is you need to say:

imageView.image = lettersPic!; 

EDIT:

So to test further, I extended the test code a little to:

if(i == NSOKButton){
    print(openPanel.URL);

    var lettersPic = NSImage(contentsOfURL: openPanel.URL!);
    print(lettersPic);
    let view:NSImageView = NSImageView();
    view.image = lettersPic

    print(view)
}

Everything still works OK. Sorry I couldn't duplicate your problem.

Upvotes: 5

jiminybob99
jiminybob99

Reputation: 873

This is the code that ended up working for me. I had to disable story boards. I had to make a class called Main. This is not to be confused with a special class called main.swift which replaces appdelegate.swift.And i also had to import Cocoa. Then I had to Specify that main inhereted from nsobject. This was so I could first make the connections between interface builder and put In ibactions and outlets in my Main.swift file.

//
//  Main.swift
//  Open
//
//  Created by ethan sanford on 2015-01-18.
//  Copyright (c) 2015 ethan D sanford. All rights reserved.
//

import Foundation
import Cocoa


class Main: NSObject{

    @IBOutlet var imageWell: NSImageCell!
    var myURL = NSURL(fileURLWithPath: "")



    @IBAction func main(sender: AnyObject) {


        imageWell.image = NSImage(byReferencingURL: myURL!)

    }


    @IBAction func open(sender: AnyObject) {
        var openPanel = NSOpenPanel();
        openPanel.allowsMultipleSelection = false;
        openPanel.canChooseDirectories = false;
        openPanel.canCreateDirectories = false;
        openPanel.canChooseFiles = true;
        let i = openPanel.runModal();
        if(i == NSOKButton){
            print(openPanel.URL);
            myURL = openPanel.URL;
        }


    }


}

it works a little strangely you have to chose your file click open. Then hit the button connected with @IBAction func main(sender: AnyObject) {

Upvotes: 1

Related Questions