loading disk image into NSImage - swift

I am trying to create a CALayer to apply to my view. But, before I even get to that I cannot get an image to load from disk.
What am I doing wrong?
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let image = NSImage(contentsOfFile: "/Users/Shared/background.png")!
let imageView = NSImageView(image: image)
self.view.addSubview(imageView)
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I have used contentsOfFile as well as byReferencingFile and it doesn't change the behavior. The file exists at that path.
UPDATE: This doesn't work either.
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let fileURL = URL(fileURLWithPath: "/Users/carroll/Desktop/background.jpg")
let image = NSImage(contentsOf: fileURL)
print(image)
let imageView = NSImageView(image: image)
self.view.addSubview(imageView)
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
So this is what is printing to the console:
Optional(<NSImage 0x600003310aa0 Size={576, 828} Reps=(
"NSBitmapImageRep 0x60000260e4c0 Size={576, 828} ColorSpace=(not yet > loaded) BPS=8 BPP=(not yet loaded) Pixels=2400x3450 Alpha=NO Planar=NO Format=(not yet loaded) CurrentBacking=nil (faulting) CGImageSource=0x600000246a60"
)>)
2020-08-10 11:46:57.767358-0500 My App[83208:8822749] Metal API Validation Enabled
2020-08-10 11:46:57.832881-0500 My App[83208:8822749] VPA info: plugin is INTEL, AVD_id = 1080010, AVD_api.Create:0x131373c15
From what I see above from the print statement it looks like it is getting the image, but it shows not loaded yet and then for the CurrentBacking it shows nil (faulting).
UPDATE 2 I was able to get the background to display, but I do not understand why this works and why I have to have set self.view.wantsLayer = true when the Apple Documentation says this property is already set to true as default.
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.wantsLayer = true
let image = NSImage(contentsOf: URL(fileURLWithPath: "/Users/Shared/background.jpg"))
self.view.layer?.contents = image
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}

Related

WKWebView shows a blank screen on OSX with no output

For some reason, the WKWebView just shows a blank screen with no errors
import Cocoa
import WebKit
class ViewController: NSViewController, WKNavigationDelegate {
#IBOutlet weak var webView: WKWebView?
override func viewDidLoad() {
super.viewDidLoad()
webView?.navigationDelegate = self
let url=URL(string: "https://www.google.com")
webView?.load(URLRequest(url: url!))
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Like I said, no errors, and it just shows a blank screen.
The storyboard looks like this:
Main.storyboard
You need to allow your application to have outgoing connections.
In your Target go to Capabilities, enable the App SandBox and check the Outgoing Connections.

Why isn't label appearing in fullscreen mode?

I have this code
import Cocoa
import AppKit
class MainScreenVC: NSViewController {
#IBOutlet var textTest: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
//let presOptions :NSApplication.PresentationOptions = [.fullScreen, .autoHideMenuBar]
//let optionsDictionary = [NSView.FullScreenModeOptionKey.fullScreenModeApplicationPresentationOptions: presOptions]
//view.enterFullScreenMode(NSScreen.main!, withOptions: optionsDictionary)
textTest.stringValue = "Hello"
}
override var representedObject: Any? {
didSet {
}
}
}
textTest has constraints to show it in the middle of the container.
When run, the app displays "Hello" in the middle of a window, as expected.
If I uncomment the lines required to enter fullscreen mode and run the app, it enters full screen, But no text is displayed.
Why?
I am using xCode 10.1
I don't know why but I had the same issues, I had come up with 2 solutions, you can either use toggleFullScreen(:) or setFrame(:display:) in the windowController windowDidLoad function
e.g.:
override func windowDidLoad() {
super.windowDidLoad()
guard let window = window,
let screen = window.screen
else { return }
window.setFrame(window.frameRect(forContentRect: screen.frame), display: true)
}

views are merged instead of showing them separately

I have two xib file, one which shows login view and another which shows the steps what to do after the login is successful. I am having hard time to make it work. I have created macos project not ios and using safariservices so that it will work for the safari extension either.
Here is what i have done
import SafariServices
class SafariExtensionViewController: SFSafariExtensionViewController {
#IBOutlet weak var passwordMessage: NSTextField!
#IBOutlet weak var emailMessage: NSTextField!
#IBOutlet weak var message: NSTextField!
#IBOutlet weak var email: NSTextField!
#IBOutlet weak var password: NSSecureTextField!
static let shared = SafariExtensionViewController()
override func viewDidLoad() {
self.preferredContentSize = NSSize(width: 300, height: 250)
message.stringValue = ""
emailMessage.stringValue = ""
passwordMessage.stringValue = ""
}
override func viewDidAppear() {
if let storedEmail = UserDefaults.standard.object(forKey: "email") as? String {
if let stepView = Bundle.mainBundle.loadNibNamed(NSNib.Name(rawValue: "ExtensionStepsViewController"), owner: nil, topLevelObjects: nil)[0] {
self.view.addSubview(stepView)
}
}
}
#IBAction func userLogin(_ sender: Any) {
let providedEmailAddress = email.stringValue
let providedPassword = password.stringValue
let isEmailAddressValid = isValidEmailAddress(emailAddressString: providedEmailAddress)
self.message.stringValue = ""
emailMessage.stringValue = ""
passwordMessage.stringValue = ""
if isEmailAddressValid && providedPassword.count > 0 {
/* login process is handled here and store the email in local storage /*
/* TODO for now email is not stored in browser localstorage which has to be fixed */
let controller = "ExtensionStepsViewController"
let subview = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: controller), bundle: nil)
self.view.addSubview(subview.view)
}
}
}
This way i get error like Type Bool has no subscript members my file structure looks something like this.
SafariExtensionViewController.xib (main one which is shown initially
with login screen)
SafariExtensionViewController.swift
ExtensionStepsViewController.xib(this view should be shown when user
is logged in instead of login screen)
ExtensionStepsViewController.swift
I am using xcode 10, swift 4, everything new.
UPDATE
I used the following block both in viewDidAppear(if there is email in localstorage then show extension steps view instead of login screen) and inside login function when the login is success but it does not navigate to that ExtensionStepsView
let controller = "ExtensionStepsViewController"
let subview = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: controller), bundle: nil)
self.view.addSubview(subview.view)
Use case is show login at initial but if user is logged in then show another view but issue is now the view are merged
You got the error "Type Bool has no subscript members" because loadNibNamed(_:owner:topLevelObjects:) method of Bundle returns Bool struct that has no subscript members so you can't write like
true[0]
How to use this method correctly see the link and example from there:
var topLevelObjects : NSArray?
if Bundle.main.loadNibNamed(NSNib.Name(rawValue: "ExtensionStepsViewController"), owner: self, topLevelObjects: &topLevelObjects) {
let topLevelObjects!.first(where: { $0 is NSView }) as? NSView
}
Views were merged because you didn't remove previous views from the superview and added view from ExtensionStepsViewController to the same superview.
You can do the following steps to complete your issue:
Make SafariExtensionViewController inherited from SFSafariExtensionViewController that will be container (and parent) for two child view controllers such as LoginViewController and ExtensionStepsViewController and will be used to navigate between ones.
Make separately LoginViewController and ExtensionStepsViewController (both inherited from simple NSViewController) and its xibs.
Right after user logins transit from LoginViewController to ExtensionStepsViewController
As an example but instead of ParentViewController you have to use your implementation SafariExtensionViewController as I explain above in the first step.
public protocol LoginViewControllerDelegate: class {
func loginViewControllerDidLoginSuccessful(_ loginVC: LoginViewController)
}
public class LoginViewController: NSViewController {
weak var delegate: LoginViewControllerDelegate?
#IBAction func login(_ sender: Any) {
// login logic
let isLoginSuccessful = true
if isLoginSuccessful {
self.delegate?.loginViewControllerDidLoginSuccessful(self)
}
}
}
public class ExtensionStepsViewController: NSViewController {
}
public class ParentViewController: NSViewController, LoginViewControllerDelegate {
weak var login: LoginViewController! // using of force unwrap is anti-pattern. consider other solutions
weak var steps: ExtensionStepsViewController!
public override func viewDidLoad() {
let login = LoginViewController(nibName: NSNib.Name(rawValue: "LoginViewController"), bundle: nil)
login.delegate = self
// change login view frame if needed
login.view.frame = self.view.frame
self.view.addSubview(login.view)
// instead of setting login view frame you can add appropriate layout constraints
self.addChildViewController(login)
self.login = login
let steps = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: "ExtensionStepsViewController"), bundle: nil)
steps.view.frame = self.view.frame
self.addChildViewController(steps)
self.steps = steps
}
// MARK: - LoginViewControllerDelegate
public func loginViewControllerDidLoginSuccessful(_ loginVC: LoginViewController) {
self.transition(from: self.login, to: self.steps, options: .slideLeft) {
// completion handler logic
print("transition is done successfully")
}
}
}
Here is a swift playground with this example.
UPD:
You can instantiate NSViewController in several ways:
Use NSStoryboard that allows to load view of NSViewController from .storyboard file:
let storyboard = NSStoryboard(name: NSStoryboard.Name("NameOfStoryboard"), bundle: nil)
let viewController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("NSViewControllerIdentifierInStoryboard"))
Use appropriate initialiser of NSViewController to load view of it from .xib file:
let steps = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: "ExtensionStepsViewController"), bundle: nil)
Use default initialiser but you have to load view directly by overriding loadView() method if name of xib file is different from name of view controller class:
let steps = ExtensionStepsViewController()
// Also you have to override loadView() method of ExtensionStepsViewController.

How to trigger a method on my container view manually?

I do have a View, in which I embedded a ContainerView. I fill my labels on my ContainerView the first time here
class UpperLower { ...
override func prepare(for segue: UIStoryboardSegue, sender: Any!) {
let PlayerInfoHeaderView = segue.destination as? PlayerInfoHeader
PlayerInfoHeaderView?.player1 = player1
PlayerInfoHeaderView?.player2 = player2
PlayerInfoHeaderView?.game = game
}
}
The Segue triggers the viewDidLoad() where I call the method updateUI()
class PlayerInfoHeader: UIViewController { ...
override func viewDidLoad() {
super.viewDidLoad()
updateUI(game: game)
}
func updateUI(game: Player.Game) {
player1NameLabel.text = player1.name
player2NameLabel.text = player2.name
switch game {
case .UpperLower:
player1PointsLabel.text = "Points: \(player1.points.UpperLower)"
player1WinrateLabel.text = "Winrate: \(player1.winrates.UpperLower) %"
player1RoundsWonLabel.text = "Rounds Won: \(player1.roundswon.UpperLower)"
player2PointsLabel.text = "Points: \(player2.points.UpperLower)"
player2WinrateLabel.text = "Winrate: \(player2.winrates.UpperLower)"
player2RoundsWonLabel.text = "Rounds Won: \(player2.roundswon.UpperLower)"
}
Now, after every round played, I also want to update my UI. I tried a lot of things, but I have no clue, how to trigger the UpdateUI() out of my UpperLower manually. I know that I need a reference to my embedded container view. But how can I get this reference outside of the segue context? Is there an easy way to solve my problem?
PS: I did all my UI work on the storyboard.
Set a weak property (to avoid retain cycle) in PlayerInfoHeader to keep a reference to your parent
weak var parentVC: UpperLower?
Set the property in prepareForSegue
PlayerInfoHeaderView.parentVC = self

Im creating a facial recognition program and I am getting a Thread BAD EXECUTION error and I don't know what to do

import UIKit
import CoreImage
class ViewController: UIViewController {
#IBOutlet weak var personPic: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
personPic.image = UIImage(named: "stupidsonny")
detect()
}
func detect() {
guard let personciImage = CIImage(image: personPic.image!) else { //This line has the error
return
}
Either the personPic outlet isn't connected, or the "stupidsonny" image isn't getting found and loaded. It's hard to tell which since they're both being accessed on the same line.
To help narrow it down, you could put a ! where you load the image:
personPic.image = UIImage(named: "stupidsonny")!