I installed Xcode 9 and I'm trying to create menu items for my MacOS app.
I have no problems in Xcode 8:
Create method
Add method to First Responder
Add new item to the menu
Drag item to first responder a select method
I'm doing the same in Xcode 9 but the item is always disabled.
In Swift 4, methods you create are not visible to Objective-C / Cocoa by default. Change
func pruebaBoton(_ sender: Any) {
to
#objc func pruebaBoton(_ sender: Any) {
Related
I tried to use Dropper to get a small Dropdown menu when a UIButton was tapped, e.g. to select a currency, but the DropperSelectedRow() was never called so I can't use its. It won't work in case of no support for swift 4 I think.
How can I implement a small and simple dropdown, which Pod should I use?
I need it for a iOS 11 App using Swift 4 and Xcode 9.
First of all:
The author of Dropper said: add
extension ViewController: DropperDelegate
when he probably wanted to say: add
extension UIViewController: DropperDelegate
see the difference between ViewController and UIViewController?
Alternatively You can use it in your view controller as delegate:
class yourViewController: UIViewController, DropperDelegate {
...
#objc func DropperSelectedRow(_ path: IndexPath, contents: String) {
print(path)
print(contents)
}
}
Don't forget #objc in front of the delegate method
Secondly:
for creating a small menu, initiate it as follow:
let dropper = Dropper(width: 80, height: 200)
change width and height as you like.
Thirdly:
It works fine with Swift 4.0
In macOS 10.12 there is a new tab bar that is added to NSWindows for NSDocument apps. You can prevent the toolbar from appearing (see How do I disable the Show Tab Bar menu option in Sierra apps?). But how to remove the "+" button for adding new Windows?
According to the AppKit release notes, returning false for responding newWindowForTab(_:) action message in a NSDocumentController subclass disables "+" button in the tab bar.
override func responds(to aSelector: Selector!) -> Bool {
if #available(OSX 10.12, *) {
if aSelector == #selector(NSResponder.newWindowForTab(_:)) {
return false
}
}
return super.responds(to: aSelector)
}
See "New Button" section in the AppKit Release Notes for macOS 10.12.
Depends of your Application functionality you may subclass NSDocumentController and return empty array for documentClassNames property.
class MyDocumentController: NSDocumentController {
override var documentClassNames: [String] {
return [] // This will disable "+" plus button in NSWindow tab bar.
}
}
Here is a documentation of the documentClassNames property:
documentClassNames
An array of strings representing the custom document classes supported by this app.
The items in the array are NSString objects, each of which represents the name of a document subclasses supported by the app. The document class names are derived from the app’s Info.plist. You can override this property and use it to return the names of document classes that are dynamically loaded from plugins.
Source
And here is explanation how documentClassNames property affects NSWindow tab bar plus button appearance:
New Button
The plus button will be shown if newWindowForTab: is implemented in the responder chain. NSDocumentController informally implements newWindowForTab:, but only returns YES from respondsToSelector: for this selector if the self.documentClassNames.count > 0 and if the app has a default new document type. In other words, it only responds to it if NSDocument has at least one registered document class name which can be edited.
Source
Just set ‘Tabbing Mode’ to Disallowed in Interface Builder for your NSWindow.
Change this
#IBAction override func newWindowForTab(_ sender: Any?) {}
into this
#IBAction func myButton(_ sender: Any?) {}
This will hide the plus button. The tabbing still works
Does someone knows why in ElCapitan GM and Xcode 7 GM the popover appears outside of the view?
The popover is triggered by the "Button".
The picture below is a new project with no code written by me, jut a button.
Is it a bug or a new "feature"?
I just tried it, and it seems that you can't set the popover anchor in the Storyboard. Perhaps this is indeed a bug in the new release.
To display the popover programmatically, set the StoryboardID of your popover view controller, for example: "PopoverViewController". Below, it's implemented in the main view controller as a lazy var, so it's instantiated just once, the first time it's referenced.
Connect an IBAction from your button to the main view controller — here, a function called "displayPopover". The "guard let" statement makes sure the sender can be cast as an NSButton.
Then, just call:
presentViewController:asPopover...
lazy var popoverViewController: NSViewController = {
return self.storyboard!.instantiateControllerWithIdentifier("PopoverViewController")
as! NSViewController
}()
#IBAction func displayPopover(sender: AnyObject) {
guard let button = sender as? NSButton else {return}
self.presentViewController(popoverViewController, asPopoverRelativeToRect: button.frame, ofView: button, preferredEdge: NSRectEdge.MaxY, behavior: NSPopoverBehavior.Transient)
}
How can I create modal slide-out window/view "in-window" in Xcode like in these screenshot?
I've tried create new Window controller with "Authentication panel style" animation but then I'm receiving only Xcode crashes.
That kind of modal window is called a Sheet. It's very easy to get this behavior with a Storyboard segue, or programmatically with an NSViewController subclass. The example below is just a blank OS X Cocoa application as created by Xcode. (I chose Swift as the language, but it will work the same way with Objective-C.)
The only things I added to the storyboard was a second View Controller for the sheet view, and a label and pushbutton on each view.
Displaying The Sheet View With A Storyboard Segue
With the Sheet View controller selected and the Connections Inspector tab displayed, connect "Presenting Segues - sheet" to the "Display Sheet" button.
Connect "Received Actions - dismissController:" to the "Close Sheet" button.
That's it! There's no code needed to make this example work; just build and run.
Displaying The Sheet View Programmatically
Note that Xcode creates the default project with two custom class files. In the Storyboard, AppDelegate.swift is represented in the Application scene:
We don't need to use the AppDelegate for this example, but you could use it for interaction with the Main Menu, or other things.
The custom ViewController.swift custom class will be used to present the sheet. It is represented in the View Controller scene:
To instantiate the Sheet View Controller programmatically, it needs a Storyboard ID. Here, we'll give it the ID "SheetViewController". Note that it's still a plain NSViewController; we don't need to make it a custom class for this example, but your application might want to:
Displaying the ViewController.swift file in the assistant editor, Ctrl-drag a connection from the "Display Sheet" button into the custom class. This will create stub code for an #IBAction function we'll name "displaySheet":
In the ViewController.swift file, we'll implement the Sheet View Controller as a lazy var. It will get instantiated only once, the first time it's accessed. That will happen the first time the displaySheet function is called.
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
lazy var sheetViewController: NSViewController = {
return self.storyboard!.instantiateControllerWithIdentifier("SheetViewController")
as! NSViewController
}()
#IBAction func displaySheet(sender: AnyObject) {
self.presentViewControllerAsSheet(sheetViewController)
}
}
Swift 4 version:
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
lazy var sheetViewController: NSViewController = {
return self.storyboard!.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "SheetViewController"))
as! NSViewController
}()
#IBAction func displaySheet(sender: AnyObject) {
self.presentViewControllerAsSheet(sheetViewController)
}
}
As in the first example, the "Close Sheet" button is connected to the "dismissController:" action on the Sheet View Controller. Alternatively, you could call that function programmatically from your ViewController class:
self.dismissController(sheetViewController)
For more information, refer to the Apple "Sheets Programming Topics" document:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Sheets/Sheets.html
Objective-C version:
- (IBAction)displaySheet:(id)sender {
NSStoryboard *storyboard = [NSStoryboard storyboardWithName:#"Main" bundle: nil];
NSViewController * vc = [storyboard instantiateControllerWithIdentifier:#"SheetViewController"];
[self presentViewControllerAsSheet:vc];
}
Specifically, I want my "New" menu item to respond to both Cmd+N and Cmd+T since it will open a new document in a tab.* How can I do this either in Interface Builder or programmatically?
* I can explain the reasoning further if needed, but I'm hoping to avoid a discussion of the merits and rather focus on how to do it, not why to do it.
Make a second one (easiest way being to duplicate it) and set it as hidden. It won't show up when the user pulls open the menu, but as long as it's enabled, its key equivalents should still be in effect.
A simple way to have two or more Key Equivalents for an action is to duplicate the NSMenuItem and add a special Tag for these "alternatives" menu items.
Then set the AppDelegate the delegate (NSMenuDelegate) of the corresponding enclosing NSMenu (where the inner items need the visibility to be updated).
Hidden menu items (or items with a hidden superitem) do not appear in
a menu and do not participate in command key matching.
When the NSMenu open, hides this alternates NSMenuItem, when it close, display them.
Example in Swift 3:
class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSApp.mainMenu?.item(withTitle: "View")?.submenu?.item(withTitle: "Zoom")?.submenu?.delegate = self
}
func toggleVisibility(_ visible: Bool, ofAlternatesKeyEquivalentsItems items: [NSMenuItem]) {
for item in items.filter({ $0.tag == 2 }) {
item.isHidden = !visible
}
}
func menuWillOpen(_ menu: NSMenu) {
if menu.title == "Zoom" {
toggleVisibility(false, ofAlternatesKeyEquivalentsItems: menu.items)
}
}
func menuDidClose(_ menu: NSMenu) {
if menu.title == "Zoom" {
toggleVisibility(true, ofAlternatesKeyEquivalentsItems: menu.items)
}
}
}