I have a macOS storyboard app in which I am having a SwiftUI view which hosted using an NSHostingController. I need to pass an EnvironmentObject to this SwiftUI view from my NSHostingController. I am unable to achieve this. I have currently written this code but it does not work. What could be the issue here?
import Cocoa
import SwiftUI
class SearchText: ObservableObject {
#Published var text = ""
}
class HostingController: NSHostingController<SwiftUIView> {
#objc required dynamic init?(coder: NSCoder) {
super.init(coder: coder, rootView: SwiftUIView().environmentObject(SearchText()))
}
}
The issue is that modifier .environmentObject returns different type than you specify in generics, ie SwiftUIView.
Here is possible approach
class HostingController: NSHostingController<AnyView> {
#objc required dynamic init?(coder: NSCoder) {
super.init(coder: coder, rootView:
AnyView(SwiftUIView().environmentObject(SearchText())))
}
}
Alternate: (Xcode 13.4)
class HostingController: NSHostingController<HostingController.HelperView> {
struct HelperView: View {
var body: some View {
SwiftUIView().environmentObject(SearchText())
}
}
#objc required dynamic init?(coder: NSCoder) {
super.init(coder: coder, rootView: HelperView())
}
}
Related
I try to add a .xib based custom view into another .xib based custom view.
The result is looking like this:
for sub in v.subviews {
Swift.print(v.subviews) // returns array [sub]
Swift.print(sub) // returns sub
Swift.print(sub.superview) // return nil!
}
How can a view be in superview's subviews, but the superview property not correctly set? Can this happen during de/coding? What do I need to set in order for this to be correct?
The next question would be, why sub is shown correctly in "View Debugging" but not in when I need it during run time.
EDIT: (thanks Matt for looking into this)
My Code looks like this:
import AppKit
func showXIBDefinedInPanel(name: String, title: String ) {
if let w = loadXIBDefined(name: name) {
let c = NSViewController()
c.view = w
let window = NSPanel(contentViewController: c)
window.isMovable = true
window.collectionBehavior = .canJoinAllSpaces
window.tabbingMode = .disallowed
window.title = title
window.styleMask = [ .titled, .utilityWindow, .closable]
window.makeKeyAndOrderFront(w)
}
}
func loadXIBDefined(name: String) -> XIBDefined? {
var topLevelObjects : NSArray?
var result : XIBDefined? = nil
if Bundle.main.loadNibNamed(NSNib.Name(rawValue: name), owner: nil, topLevelObjects: &topLevelObjects) {
result = topLevelObjects!.first(where: { $0 is XIBDefined }) as? XIBDefined
}
return result
}
///used to embed a XIBDefined into another XIB
#IBDesignable class XIBEmbedder : NSView {
// Our custom view from the XIB file
var view: NSView!
var xibName: String!
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
view = loadXIBDefined(name: xibName)
addSubview(view)
self.frame = view.frame
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
view = loadXIBDefined(name: xibName)
addSubview(view)
self.frame = view.frame
}
init(name: String) {
self.xibName = name
super.init(frame: NSZeroRect)
view = loadXIBDefined(name: xibName)
addSubview(view)
self.frame = view.frame
}
}
///used as class for XIB based Custom Views
#IBDesignable class XIBDefined: NSView {
///I had an issue with an oddly moved frame, so I hard coded a fix
override func setFrameOrigin(_ newOrigin: NSPoint) {
super.setFrameOrigin(NSZeroPoint)
needsDisplay = true
}
}
#IBDesignable class WelcomeCMapView : XIBEmbedder {
init() {
super.init(name: "Welcome Concept Maps")
}
required init?(coder decoder: NSCoder) {
super.init(name: "Welcome Concept Maps")
}
}
Key are the two classes XIBDefined and XIBEmbedder. Former one can be loaded from a XIB, the latter can be used in an XIB. Therefore the later embedded XIB uses XIBDefined as the custom class, the embedding XIB has a WelcomeCMapView as custom class.
The problem-showing code on top is part of the post-processing, which is executed within viewDidLoad() from the NSViewController loading the embedding XIB.
I've just updated my code from Swift 1.2 to Swift 2.1. The project was fully-functioning with previous version of Swift, but now I'm seeing "Ambiguous use of 'init'" errors. Each occurrence of this error seems to be caused by the use of optional arguments in the constructor. I've even managed to reproduce this issue in Xcode Playground with the following simplified code using the same pattern:
class Item : UIView {
override init(frame: CGRect = CGRectZero) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let i = Item() # Ambiguous use of 'init(frame:)'
However, I still don't understand why Swift 2.1 now has a problem with this pattern, or what the alternative should be. I've tried searching for this, but all I've stumbled upon was "ambiguous use" errors for other (non-constructor) methods due to method renaming or signature changing, neither of which is the case here.
It's ambiguous use because when u call let i = Item(), there are 2 options -
the init(frame: CGRect = CGRectZero) and the init()
it's better to do something like this:
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init() {
self.init(frame: CGRectZero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
I'm getting the following error:
Type 'TestClass' does not conform to protocol 'NSCoding'
There's only two methods required for NSCoding and both are there. What am I missing?
class TestClass: NSObject, NSCoding {
var Property1:Double? = 0.00
required init(code aDecoder: NSCoder) {
if let priceCoded = aDecoder.decodeObjectForKey("Property1") as? Double {
self.Property1 = priceCoded
}
}
func encodeWithCoder(aCoder: NSCoder){
if let priceEncoded = self.Property1 {
aCoder.encodeObject(priceEncoded, forKey: "Property1")
}
}
}
You missed 'r' symbol in an argument name. Update your initializer name to:
required init(coder aDecoder: NSCoder)
And it will work
I am solving this by using this :-
required init?(coder aDecoder: NSCoder) {
}
func encodeWithCoder(aCoder: NSCoder) {
}
XCode 8.0 (8A218a) , swift 3
I'm working on an OS X App and, I can't seem to get Drag and Drop to work. I've Googled a lot, but most posts about this subject are at least a few years old and none of them tells me the missing link I have in my thoughts.
Anyway, here is what I'm trying to do. I have an image somewhere on my desktop and I want the ability to drag and drop that into my Custom NSView. The custom view is a child object of a custom NSView named CircularImageView and is layer backed and only shows a circular shaped image on the screen.
Here's the code:
import Cocoa
import MCTools
#objc public protocol DragAndDropCircularImageViewDelegate {
func imageDumped(sender: AnyObject!)
}
#IBDesignable #objc public class DragAndDropCircularImageView: CircularImageView {
// This class provides the Drag And Drop Feature to the CircularImageView Class.
// MARK: New in this class
var highlight: Bool = false
public var delegate: DragAndDropCircularImageViewDelegate?
private func registerForDraggedImages() {
self.registerForDraggedTypes(NSImage.imageTypes())
}
// MARK: CircularImageView Stuff
public override var image: NSImage? {
didSet {
if let newImage = image {
delegate?.imageDumped(self)
}
}
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
self.registerForDraggedImages()
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.registerForDraggedImages()
}
public override func updateLayer() {
super.updateLayer()
if highlight == true {
}
}
// MARK: NS Dragging Destination Protocol
public override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
// When a drag enters our drop zone.
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
if ((sender.draggingSourceOperationMask().rawValue & NSDragOperation.Copy.rawValue) > 0) {
highlight = true
self.needsLayout = true
sender.enumerateDraggingItemsWithOptions(.Concurrent, forView: self, classes: [NSPasteboardItem.self], searchOptions: [NSPasteboardURLReadingContentsConformToTypesKey: self], usingBlock: { (draggingItem, idx, stop) -> Void in
return
})
}
return NSDragOperation.Copy
}
return NSDragOperation.None
}
public override func draggingExited(sender: NSDraggingInfo?) {
// When drag exits our drop zone remove highlight of the drop zone.
println("\(self)draggingExited")
highlight = false
self.needsLayout = true
}
public override func prepareForDragOperation(sender: NSDraggingInfo) -> Bool {
// Update view for hovering drop.
println("\(self)prepareForDragOperation")
highlight = false
self.needsLayout = true
// Can we accept the drop?
return NSImage.canInitWithPasteboard(sender.draggingPasteboard())
}
public override func performDragOperation(sender: NSDraggingInfo) -> Bool {
// Handle the drop data.
println("\(self)performDragOperation \(sender)")
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
self.image = NSImage(pasteboard: sender.draggingPasteboard())
}
return true
}
// MARK: Interface Builder Stuff
}
I have seen some posts that I should be using:
self.registerForDraggedTypes([NSFilenamesPboardType])
instead of:
self.registerForDraggedTypes(NSImage.imageTypes())
But this doesn't seem to work in my case, when I'm using NSFileNamesPboardType I get the following debug message even before any of the NSDraggingDestination protocol messages have been called:
2015-05-07 11:07:19.583 CircularImageViewTest[44809:14389647] -[CircularView.DragAndDropCircularImageView copyWithZone:]: unrecognized selector sent to instance 0x608000166d80
(lldb) p 0x608000166d80
(Int) $R0 = 106102873550208
I don't understand how this works. Somewhere the frameworks try to copyWithZone on an integer? Can anyone explain this to me?
Any help would be appreciated. Thanks in advance.
Ok, the code below works. It was all caused by sender.enumerateDraggingItemsWithOptions in draggingEntered. Something goes wrong in the Apple frameworks when it is called.
import Cocoa
import MCTools
#objc public protocol DragAndDropCircularImageViewDelegate {
func imageDumped(sender: AnyObject!)
}
#IBDesignable #objc public class DragAndDropCircularImageView: CircularImageView {
// This class provides the Drag And Drop Feature to the CircularImageView Class.
// MARK: New in this class
var highlight: Bool = false
public weak var delegate: DragAndDropCircularImageViewDelegate?
private func registerForDraggedImages() {
// self.registerForDraggedTypes(NSImage.imageTypes())
self.registerForDraggedTypes([NSFilenamesPboardType])
}
// MARK: CircularImageView Stuff
public override var image: NSImage? {
didSet {
if let newImage = image {
delegate?.imageDumped(self)
}
}
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
self.registerForDraggedImages()
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.registerForDraggedImages()
}
public override func updateLayer() {
super.updateLayer()
if highlight == true {
}
}
// MARK: NS Dragging Destination Protocol
public override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
// When a drag enters our drop zone.
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
if ((sender.draggingSourceOperationMask().rawValue & NSDragOperation.Copy.rawValue) > 0) {
highlight = true
self.needsLayout = true
}
return NSDragOperation.Copy
}
return NSDragOperation.None
}
public override func draggingExited(sender: NSDraggingInfo?) {
// When drag exits our drop zone remove highlight of the drop zone.
println("\(self)draggingExited")
highlight = false
self.needsLayout = true
}
public override func prepareForDragOperation(sender: NSDraggingInfo) -> Bool {
// Update view for hovering drop.
println("\(self)prepareForDragOperation")
highlight = false
self.needsLayout = true
// Can we accept the drop?
return NSImage.canInitWithPasteboard(sender.draggingPasteboard())
}
public override func performDragOperation(sender: NSDraggingInfo) -> Bool {
// Handle the drop data.
println("\(self)performDragOperation \(sender)")
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
self.image = NSImage(pasteboard: sender.draggingPasteboard())
self.delegate!.imageDumped(self)
}
return true
}
// MARK: Interface Builder Stuff
}
Say I have a class like this one:
class UniverseViewController: UITableViewController {
var model = createModel()
// blah, blah...
}
When exactly will the createModel function be called? Before the init? After it?
It is called before init and viewDidLoad etc.
The following code:
struct testStruct {
init() {
println("testStruct")
}
}
let tempValue = testStruct()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
println("Coder")
}
override func viewDidLoad() {
super.viewDidLoad()
println("viewDidLoad")
}
will give us the following output:
testStruct
Coder
viewDidLoad