XCode: Swift strange behaviour and errors - swift

I'm new to Swift, I'm trying to create a new Cocoa Application based on the following code:
Drag and Drop in Swift - Issues with Registering for Dragged Types?
I'm doing this:
create Swift Cocoa Application
add a Custom View
add a Swift Class subclass of NSView named DropView
set the Custom View class to DropView
paste the referenced code in DropView
Then I'm getting a few errors, and I'm not really understanding why and how to solve it. Can someone help me?

As the first error says, you need to add the override to your init method:
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
Also there are some changes in the draggingEntered and the draggingUpdated methods. You don't need the ! for your NSDraggingInfo anymore:
override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
return NSDragOperation.Copy
}
override func draggingUpdated(sender: NSDraggingInfo) -> NSDragOperation {
return NSDragOperation.Copy
}
To check the correct implementation of a method you want to override, just start typing the method-name in your file and Xcode will create you the method with header etc:
To fix your last error, you need to implement the required init the NSView needs:
required init?(coder: NSCoder) {
super.init(coder: coder)
}
To help you for further errors like these: Often Xcode itself helps you to solve the problems. Especially if you see the round error-circle on the left side of your editor. Just click on it and check what Xcode recommends:

Related

Why do these NSView swift subclesses allow initialisation to run twice

In the Swift subclasses below, when ChildView(dummy: NSObject()) is called, two different designated initializers for ParentView will be invoked, and the myLayer ivar of ParentView will be initialized twice.
Not that this will not happen in a Playground - that will correctly report that error: MyPlayground.playground:5:31: error: must call a designated initializer of the superclass 'NSView'
However, in an Xcode project (e.g. a Command Line Tool), this will compile and run fine.
I understand a lot of what's going on, and how to prevent this problem. But I'd love to understand why the compiler is not erroring, or at least warning me about this code (and preventing the double initialisation)
This code compiles cleanly under Swift 4.2, and in the debugger, I can see exactly what's happening:
When ChildView(dummy:) is called, it invokes ParentView(dummy:)
That's a designated initialiser, do the ParentView ivars are also initialised (myLayer)
Then ParentView(dummy:) calls super.init(), which is NSView.init()
The implementation of NSView.init() calls initWithFrame, so ChildView.init(frame:) gets invoked, and invokes ParentView.init(frame:). That's also a designated initialiser, so
myLayer gets initialised a second time.
To demonstrate this, given the classes below, invoke
let _ = ChildView(dummy: NSObject)
>>>> creating layer will get printed twice.
The way to avoid this would be to have ParentView(dummy:) call super.init(frame:), and then the problem goes away.
My question is really, how could this ever happen in the first place?
import Cocoa
class ParentView: NSView {
private var myLayer: CALayer = {
print(">>>> creating layer")
return CALayer()
}()
override init(frame frameRect: NSRect) { super.init(frame: frameRect) }
required init?(coder decoder: NSCoder) { super.init(coder: decoder) }
init(dummy: AnyObject?) { self.init() }
}
class ChildView: ParentView {
override init(frame frameRect: NSRect) { super.init(frame: frameRect) }
required init?(coder decoder: NSCoder) { super.init(coder: decoder) }
override init(dummy: AnyObject?) { super.init(dummy: dummy) }
}
This should generate a compile time error, as it does in a Playground.
I can't seem to construct a similar non-NSView class hierarchy that gives the same behaviour. No matter how I tweak the hierarchy, constructors, etc., Swift always gives compiler errors when I try to this with new objects (even when I mix Objective-C and Swift, to be analagous to NSView.
I can't find any public declaration for NSView.init() either, although I can see it executing in the debugger.

I get error - Use of undeclared type 'BaseCell' - while trying to create a Cell file in Swift following a tutorial

I am a new programmer (self taught) and am following a youtube tutorial ( https://www.youtube.com/watch?v=PNmuTTd5zWc&t=21s ) to learn how to program a UICollectionView that appears bottom up for my app.
When I try to create the file for the BaseCell in minute 4:50, I don't get the error.
Use of undeclared type 'BaseCell'
This is what it's supposed to look like according to the tutorial
Am I getting this error because it doesn't exist in Swift anymore or what might be a possible error?
Could someone help me out?
the base cell class is a custom class of type UIcollection view cell
class BaseCell: UICollectionViewCell
{
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
func setUpView()
{
}
required init?(coder aDecoder: NSCoder) {
fatalError("init (coder:) has been implemented")
}
}
if you get the error Use of undeclared type 'BaseCell' then build your project again it will work.

Method does not override any method from it's superclass in Swift3?

override func layoutAttributesForElements(in rect: CGRect) -> [AnyObject]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect ) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes }
Here I'm getting the error.
Method does not override any method from it's superclass.
How can I resolve the issue.
Any one can you please help me.
Thanks in Advance.
If you are really trying to override that method in its right place, Try removing the method name and type override func layoutAttributes and select the correct override from auto complete. These kind of problems sometimes occur because of Swift's syntax changes in different versions of swift
Try removing the override should work.

ViewController + Storyboard setting up validation with controlTextDidChange

Trying to setup validation for a few text fields in a new (and very small) Swift Mac app. Following various other topics here on SO and a few other examples, I can still not get controlTextDidChange to propagate (to my ViewController).
E.g: How to live check a NSTextField - Swift OS X
I have read at least a dozen variations of basically that same concept. Since none of the accepted answers seem to work I am just getting more and more confused by something which is generally a fairly simple task on most platforms.
I have controlTextDidChange implemented to just call NSLog to let me know if I get anything.
AppDelegate should be part of the responder chain and should eventually handle controlTextDidChange but I see nothing there either.
Using the current Xcode I start a new project. Cocoa app, Swift, Storyboard and nothing else.
From what I can gather the below isolated example should work. In my actual app I have tried some ways of inserting the ViewController into the responder chain. Some answers I found suggested it was not always there. I also tried manually adding the ViewController as the delegate in code theTextField.delegate = self
Nothing I have done seems to get text changed to trigger any events.
Any ideas why I have so much trouble setting up this delegation?
My single textfield example app
Storyboard is about as simple as it gets:
AppDelegate
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSTextFieldDelegate, NSTextDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func controlTextDidChange(notification: NSNotification) {
let object = notification.object as! NSTextField
NSLog("AppDelegate::controlTextDidChange")
NSLog("field contains: \(object.stringValue)")
}
}
ViewController
import Cocoa
class ViewController: NSViewController, NSTextFieldDelegate, NSTextDelegate {
#IBOutlet var theTextField: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func controlTextDidChange(notification: NSNotification) {
let object = notification.object as! NSTextField
NSLog("ViewController::controlTextDidChange")
NSLog("field contains: \(object.stringValue)")
}
}
I think the samples you're following are a bit out-of-date.
Try...
override func controlTextDidChange(_ notification: Notification) {
...as the function definition for your method in your NSTextFieldDelegate.

NSViewController User Interface State Restoration

Summary:
What's the proper way to save/restore the state of an NSSearchField in my NSViewController using the built-in user interface preservation mechanism of Cocoa?
Details:
I'm working on my first macOS app and I'm having a little trouble with state restoration for my user interface. So far I have it working to the point where the encodeRestorableState(with:) and restoreState(with:) methods are called in my NSViewController subclass.
I have an NSSearchField in my view controller and I want to save/restore the state of the search field including its text, any selection, the cursor position, and whether it is currently in focus or not.
If I use the following code, the text is properly saved and restored:
override func encodeRestorableState(with coder: NSCoder) {
super.encodeRestorableState(with: coder)
coder.encode(searchField.stringValue, forKey: "searchText")
}
override func restoreState(with coder: NSCoder) {
super.restoreState(with: coder)
if let searchText = coder.decodeObject(forKey: "searchText") as? String {
searchField.stringValue = searchText
}
}
Obviously I can add more code to save/restore the search field's selection and cursor position, etc.
My real question is, is there a better, proper, more automatic way to save and restore the search field's state? Or is it required that I write my own code for each attribute of the search field I wish to save?
I tried using:
searchField.encodeRestorableState(with: coder)
and:
searchField.restoreState(with: coder)
in the two above methods but that didn't result in anything appearing in the search field when my app was restarted.
I also implemented:
override class func restorableStateKeyPaths() -> [String] {
var keys = super.restorableStateKeyPaths()
keys.append("searchField.stringValue")
return keys
}
where "searchField" is the name of the NSTextField outlet property in my view controller. This method is called when my app is launched but the text was not restored in the search field.
This view controller is created from a storyboard. The view controller's identifier is set. The search field's identifier is set as well. This view controller is a child view controller of another view controller.
I've read through the User Interface Preservation section of the "The Core App Design" document but it's unclear on how a view controller's views are saved/restored and how much of this is automatic versus manual.
Supporting OSX 10.12 and 10.11. Objective-C or Swift in any answers is fine.
To achieve aforementioned effect let's create NSSearchField and a custom class named RestoredWindow containing just one property:
import Cocoa
class RestoredWindow: NSWindow {
override class var restorableStateKeyPaths: [String] {
return ["self.contentViewController.searchField.stringValue"]
}
}
Assign this custom class to Window Controller in Identity Inspector.
Next, let's bind searchField.stringValue property to ViewController in Value section of Bindings Inspector.
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var searchField: NSSearchField!
override func viewDidLoad() {
super.viewDidLoad()
}
}
After this, make sure you haven't checked Close windows when quitting an app option in your System Preferences -> General tab.
Now, entered text in NSSearchField restored after quitting and launching the app again.
P.S.
I've tried the same way as you but failed too. This approach doesn't work for me:
override func encodeRestorableState(with coder: NSCoder) {
coder.encode(searchField.stringValue, forKey: "restore")
super.encodeRestorableState(with: coder)
}
override func restoreState(with coder: NSCoder) {
if let state = coder.decodeObject(forKey: "restore") as? NSSearchField {
searchField.stringValue = state
}
super.restoreState(with: coder)
}