I have some trouble with my slider, when I put it on View, it isn't work.
I create simple interface:
First slider in my interface isn't work, but second is work perfectly.
I found that in my Xcode nothing work in View, buttons, slider and etc.
ViewController
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
XCode Version - 11.3.1
MacOS Version - 10.15.1
I fix it, you only need to set height for your View.
Related
I created a Mac OS X application in Xcode using storyboards. For some reason the applicationDidFinishLaunching method in the AppDelegate is being called after viewDidLoad in the NSViewControllers. As with iOS apps, I thought viewDidLoad is supposed to be called before applicationDidFinishLaunching? Do storyboards in OS X apps initialize the view controllers before the app has finished launching?
I am using the applicationDidFinishLaunching method to register default settings into NSUserDefaults. Unfortunately, registering the default values is happening after the views in the storyboard are loaded. Therefore, when I set up the view in each view controller using viewDidLoad, the defaults data in NSUserDefaults has not been set. If I can't use applicationDidFinishLaunching to register NSUserDefaults in OS X storyboard apps, then how I set the defaults before viewDidLoad is called?
To fix this issue, in the Main.storyboard in Xcode, I turned off "Is Initial Controller" for the main window. I assigned a storyboard ID to the main window as "MainWindow". Then in the AppDelegate I entered the following code:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(aNotification: NSNotification) {
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let mainWindow = storyboard.instantiateControllerWithIdentifier("MainWindow") as! NSWindowController
mainWindow.showWindow(nil)
mainWindow.window?.makeKeyAndOrderFront(nil)
}
}
The app does not crash but now the window never appears. The following image displays the storyboard I'm working with:
Correct, the lifecycle is a wee bit different in OS X.
Instead of letting the storyboard be your initial interface (this is defined in the General settings of your project), you can instead set up a MainMenu xib file and designate that as your main interface, then in your applicationDidFinishLaunching method in your AppDelegate you can programmatically instantiate your storyboard after you have completed your other initialization code.
I recommend checking out Cocoa Programming for OS X: The Big Nerd Ranch Guide if you haven't already; one nice thing they do in their book is actually have you get rid of some of the default Xcode template stuff and instead they have you set up your initial view controller the "right" way by doing it explicitly.
You might put something like this in your applicationDidFinishLaunching:
NSStoryboard *mainStoryboard = [NSStoryboard storyboardWithName:#"Main" bundle:nil];
MyWindowController *initialController = (MyWindowController *)[mainStoryboard instantiateControllerWithIdentifier:#"myWindowController"];
[initialController showWindow:self];
[initialController.window makeKeyAndOrderFront:self];
This assumes that you've already changed "Main Interface" to something like MainMenu.xib.
As the question's author mentioned:
The app does not crash but now the window never appears.
Despite Nicolas and Aaron both helpful answers (I already adquire the book you recommended, Aaron, and will start implementing the two-storyboard pattern your way, Nicolas), neither solve the specific problem of the window not showing.
Solution
You need make your WindowController instance live outside applicationDidFinishLaunching(_:)'s scope. To accomplish it, you could declare a NSWindowController propriety for your AppDelegate class and persist your created WindowController instance there.
Implementation
In Swift 4.0
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var mainWindowController: NSWindowController?
func applicationDidFinishLaunching(_ aNotification: NSNotification) {
let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
let mainWindow = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "MainWindow")) as! NSWindowController
mainWindow.showWindow(self)
mainWindow.window?.makeKeyAndOrderFront(self)
// After applicationDidFinishLaunching(_:) finished, the WindowController instance will persist.
self.mainWindowController = mainWindow
}
}
In addition to Aaron's answer, I found out that the separate Interface Builder file containing the menu alone (separate from the window and view controller) does not need to be a xib; it too can be a storyboard. This enables us to easily fix as follows:
Duplicate your original storyboard (Main.storyboard) and rename it to "Menu.storyboard"
Delete the window controller and view controller from Menu.storyboard
Delete the app menu bar from Main.storyboard.
Change your app target's "Main Interface" from "Main.storyboard" to "Menu.storyboard".
(I tried it in my app and it works)
This avoids having to re-create the app's main menu from scratch, or to figure out how to transplant your existing one from "Main.stroyboard" to "Menu.xib".
The other solution is registering NSApplicationDidFinishLaunchingNotification in your view controllers and move your code from viewDidLoad to applicationDidFinishLaunchingNotification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidFinishLaunchingNotification:)
name:NSApplicationDidFinishLaunchingNotification
object:nil];
Based on my previous answer here
If you want a piece of code to be run before everything, you can override the AppDelegate's init() like this:
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
DoSomethingBeforeEverthing() // You code goes here
super.init()
}
...
}
Few points to remember:
You might want to check, what's allowed/can be done here, i.e. before app delegate is initialized
Also cleaner way would be, subclass the AppDelegate, and add new delegate method that you call from init, say applicationWillInitialize and if needed applicationDidInitialize
I am working on a Yosemite app in swift and have hit a road block.
I have multiple views working properly, and now I want to implement custom menu actions. To keep the answer simple, how would I achieve this example. I want to click a menu button and have it change text on the viewcontroller. I have tried setting up IBActions, but I'm not sure how to make the link to the viewcontroller from the AppDelegate. How do you connect the two?
I'm still figuring this stuff out, so any insight would be awesome. Thanks in advance.
*UPDATE. I tried making a object and linking it that way. No luck.
When you press "Test" it prints test, however it's in it's own class. I need to do something in my main ViewController class. How to I make that reference?
Getting NSViewController from NSWindow is an easy solution.
If your app has multiple windows, select appropriate one through keyWindow or windows of NSApplication.
#IBAction func pressed(sender: AnyObject) {
if let window = NSApplication.sharedApplication().mainWindow {
if let viewController = window.contentViewController as? YourViewController {
// do stuff
...
}
}
}
I've been trying to build on a Cocoa app that uses Swift and Storyboard in Xcode 6, but how can I use NSToolbar there?
In Xcode 5 and xib, you can add NSToolbar from within Object Library to any .xib files and then click on the added toolbar to expand it and drag and drop a connection from any items there to a AppDelegate.h file. In this way you can create a IBAction or IBOutlet connection. I confirmed that this can also be done if you use Swift and non-storyboard in Xcode 6. However, it looks like this is not the case in Xcode 6 and Storyboard environment.
I first created a project that uses Storyboard in Xcode 6, but then, I wasn't able to add a NSToolbar from within Object Library to a View Controller in Storyboard. So I added it to Window Controller's Window object in Storyboard. However, in this way I cannot create those connections from any items in the expanded toolbar to either AppDelegate.swift or ViewController.swift.
So my question is:
Is it feasible to create a storyboard app that uses NSToolbar?
If it is feasible, is the addition of NSToolbar to the Window Controller the proper way to use NSToolBar in Storyboard?
Finally, how can I create #IBOutlet and #IBAction connections there?
UPDATE
I found that the accepted answer by #GeorgeVillasboas only works for #IBAction. I am still looking for how to create an #IBOutlet connection...
I had this very same problem.
The solution works for both Objective-C and Swift projects.
Working with Storyboards on OSX, it creates an instance of NSWindow and segues to another NSViewController as its Window Content Segue, as you described.
On your ViewController, create a standard IBAction to receive the action when the toolbar is clicked. To wire it up with the NSToolbar, just control-drag (or leftClick-drag) from your NSToolbarItem to the FirstResponder object, as shown on the picture below.
This will open a HUGE list of available connections. Your IBAction will be on that list.
Just selected it and you're good to go.
Hope this helps!
Here's an answer that doesn't rely on run-time hook-ups - #cdalvaro's answer gets most of the way there for some applications, but isn't full, and it requires the ViewController to know about the artificial NSWindowController, which doesn't feel right.
Like #cdalvaro, the first step is to build your own subclass of NSWindowController, and to set the Storyboard WC to that class. You can then create all of your connections to and from the NSToolbar (both #IBOutlets & #IBActions) in the new WindowController. So far so good.
The last step, which I haven't seen anywhere else, is how to refer to the ViewController in the WindowController - you can't create an #IBOutlet to it - for the same reasons that we got here in the first place - you can't create references across scenes in the Storyboard. However, the WindowController must have a reference to the ViewController, and it does... self.window!.contentViewController! as! ViewController
Here's a complete WindowController with a checkbox that sets values in the ViewController, without the ViewController having to know anything...
class MyWindowController: NSWindowController {
var viewController: ViewController {
get {
return self.window!.contentViewController! as! ViewController
}
}
#IBOutlet weak var aSwitch: NSButton!
#IBAction func toolbarActionA(sender: AnyObject) {
println("toolbarActionA")
self.viewController.a = !self.viewController.a
self.aSwitch.state = self.viewController.a ? NSOnState : NSOffState
}
}
This helped me for the IBOutlet solution you are looking for:
https://stackoverflow.com/a/27555237/3398062
Update (explanation)
I discovered this thread because I was trying to create a Circular Progress Indicator inside the toolbar and I didn't know how to animate it from the ViewController since the IBOutlet was declared inside a custom WindowController.
Finally, I found the post that I have added above which describes how to access to IBOutlets from other classes.
In essence what I have done is the following:
Create a custom NSWindowController subclass (CustomWindowController) for the Window Controller so I can add the IBOutlet for the ProgressIndicator:
Code Example:
#interface CustomWindowController : NSWindowController
#property (weak) IBOutlet NSProgressIndicator *progressIndicator;
#end
Then in the ViewController class, in the method I want to use to update the state of the Progress Indicator, I create and object of the custom Window Controller.
Code Example:
CustomWindowController *customWindowController = self.view.window.windowController;`
Finally, to change the state of the Progress Indicator there is only to call the method from the created custom object.
Code Example:
[customWindowController.progressIndicator startAnimation:sender];
or
[customWindowController.progressIndicator stopAnimation:sender];
This video helped me how to create a toolbar without writing a single line of code: https://www.youtube.com/watch?v=XSQocHG3IjA
You can add the 'Window Controller' item from the Object Library (if you don't have one), connect to a View Controller (where you want your toolbar to display) and follow the video! For custom Toolbar buttons add 'Image Button' item to the Toolbar just by dragging from the Object Library. Then you can pick an image for a button, set the size and so on...
Here is a general solution for the outlets and actions. it allows you to preform all the the same functions as an iboutlet would for a tool bar item and also allows you to set the button to a function instead of creating an ibaction. hope it helps :P
override func viewDidLayout() {
var x = self.view.window?.toolbar?.items[1].label
println(x)
if(self.view.window?.toolbar?.items[0].label! != "Check")
{
toobarediting()
}
println("didlay")
}
func toobarediting() {
self.view.window?.toolbar?.insertItemWithItemIdentifier("Check", atIndex: 0)
}
func toolbarcheck(functiontoset: Selector) {
var y = self.view.window?.toolbar?.items[0] as NSToolbarItem
y.action = functiontoset
if(functiontoset != nil)
{
y.enabled = true
}
}
Here is a link to my question attempting to get a cleaner answer
http://www.stackoverflow.com/questions/27371439/use-nstoolbar-outlet-xcode-6-and-storyboard/27374449
but it looks like from the answers i have gotten so far this is the best option.
The same problem of IBOutlets also applies to KVO. One of the features of the NSSplitViewController is the canCollapse property for each split view item which supports KVO, but this is unusable in IB just like IBOutlets.
The only workaround I can see is to add a NSObjectController to each scene and when the scene loads, set the object controller's object to the object in the other scene (already loaded) that you want to reference.
I've tried to build a sample Cocoa app on which I want to connect UI components put on storyboard to ViewController.swift as either an IBOutlet or IBAction. However, when I tried to control-drag the UI components on storyboard (such as NSButton) to ViewController.swift and create a #IBAction method, and then run the app, the resultant app logs the following message in console and of course the app doesn't respond to me tapping the button.
Failed to connect (storyboard) outlet from (NSApplication) to (NSNibExternalObjectPlaceholder): missing setter or instance variable
How can I use the IBAction method properly?
For your information here's my ViewController.swift:
import Cocoa
class ViewController: NSViewController {
#IBOutlet var txtTitle : NSTextField
#IBOutlet var boxColor : NSBox
override func viewDidLoad() {
super.viewDidLoad()
}
func colorChanged(cp: NSColorPanel) {
let c:NSColor = cp.color;
self.boxColor.fillColor = c
}
#IBAction func btnSetColor(sender : AnyObject) {
let cp:NSColorPanel = NSColorPanel.sharedColorPanel()
cp.setTarget(self)
cp.setAction("colorChanged:")
cp.orderFront(nil)
}
#IBAction func btnSetWindowTitle(sender : AnyObject) {
if self.txtTitle.stringValue != "" {
println(self.title)
println(self.txtTitle.stringValue)
self.title = self.txtTitle.stringValue
}
}
}
I use Xcode 6 beta on OS X 10.10 Yosemite. And started the template with storyboard being on.
While the answer above correctly states that this isn't the reason for compilation issues, I thought that I would clarify for those who are just looking to eliminate the warning messages altogether. This was what I was looking for when I found this page.
When you are building your actions and some of the actions change, or get deleted in the storyboard, the outlets remain. Select the controller/window where the older unused actions used to be and you will still see them in the outlets segment of the storyboard within the attributes tab. Remove those old actions/outlets there and then the warning disappear.
Look for duplicates between the ViewController and the File's Owner. One or both might be holding on to these objects when they shouldn't be. Removing those will remove these soft warnings.
Failed to connect (storyboard) outlet from (NSApplication) to (NSNibExternalObjectPlaceholder): missing setter or instance variable
The IBAction methods working like it should, see Apple Dev Forums:
"This is a known issue ... The messages are harmless and do not
indicate a problem with your code."
Apple Dev Forums: OS X Storyboard failure
Thats not why your code is not working, you need to fix the following:
A) Here is my working code to set the title - using self.view.window.title instead self.title:
#IBAction func btnSetWindowTitle(sender : AnyObject) {
if self.txtTitle.stringValue != "" {
println(self.view.window.title)
println(self.txtTitle.stringValue)
self.view.window.title = self.txtTitle.stringValue
}
}
B) In Interface Builder you need to set NSBox "Box Type" to "Custom":
And that's it:
I think I figured out the right solution.
1) Drag an Object into you xib interface.
2) Click the Object in the left list you just dragged in.
3) Bind the Object to your custom class.(Here my class is a login window controller as example)
4) Ctrl drag button to the source code. In the popup window, choose your class name(here in example is Login Window Controller) rather than File's Owner.
Hope this could help you.
I've found another easier solution these days while coding.
Check this out.
1) Select File's Owner in Document Outline in the .xib file.
2) Specify the class you want the .xib file to connect with.
3) Now when you connect outlet to the source file, just use default File's Owner. Much easier.
4) I guess it's not enough so far. I've met an exception when running called 'loaded the 'xxx' nib but the view outlet was not set'. We should do something more.
Select the view in Document Outline. Drag from the circle of New Referencing Outlet to the File's Owner in Document Outline.
Alright, that's the new easier solution. No additional objects should add into the xib. If it doesn't work, leave comments below.
I've just played around with the new mac storyboard-feature included in Xcode 6. I've set up a new OS X-project using storyboards and swift, then I've created a new file MainWindowController.swift, created the initializer init(coder: NSCoder!) (because otherwise the compiler warns me) and hooked everything up in the Main.storyboard file (set the MainWindowController-class for the WindowController in the inspector).
Everything compiles fine, my Window with the specified window content-view opens. But the code I've written in the windowDidLoad-function is never be called. Let it just something like:
override func windowDidLoad() {
super.windowDidLoad()
println("Executed")
}
I've also tested if my initializer is called - it is.
Does anybody has a clue? I've never used storyboards intensively on iOS before, maybe I miss something substantial.
In Yosemite, NSViewController has been promoted with powerful new features to make it work with Storyboards. Meanwhile, NSWindowController got demoted. With Storyboards, windows are no longer loaded from a nib, so windowDidLoad() doesn't get called anymore.
It makes sense for the window itself to become less important, in favor of a more powerful view it actually contains. My other answer on this page shows how to set up an AppDelegate to customize the window appearance. There's more detail on another page here, about using an AppDelegate to implement some of the things you might previously have done in an NSWindowController.
However, if you only wanted to catch windowDidLoad() as a way to customize the appearance options of the window, it is very easy to do that in Interface Builder, by simply adding them as User Defined Runtime Attributes to the NSWindow object. You don't need to subclass NSWindowController or write any code at all. Just plug in these values to the NSWindow object via the Identity Inspector pane to achieve the same effect shown in the AppDelegate example code:
Keypath: titlebarAppearsTransparent, Type: Boolean, Value: Checked
Keypath: titleVisibility, Type: Number, Value: 1
Keypath: styleMask, Type: Number, Value: 32783
Look in the headers to determine the actual numeric values of the constants.
( for example: NSWindowTitleVisibility.Hidden = 1 )
Of course, you can't specify individual bits of the styleMask, but it's easy enough to add them all together and get a single number to specify the style.
Remove everything except the Application scene from the Main.storyboard file, and instead, create a new Application.storyboard for the application window. Implement an application delegate class and connect it to the Application object in Main.storyboard. Use this class to instantiate the window controller and set up custom options for the application window.
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidBecomeActive(notification: NSNotification) {
let storyboard = NSStoryboard(name: "Application", bundle: nil)
applicationController = storyboard.instantiateInitialController() as? NSWindowController
if let window = applicationController?.window {
window.titlebarAppearsTransparent = true
window.titleVisibility = NSWindowTitleVisibility.Hidden
window.styleMask |= NSFullSizeContentViewWindowMask
applicationController!.showWindow(self)
}
}
}
Rather than subclassing NSWindowController, use the ViewController.swift subclass of NSViewController that Xcode creates for you automatically with the project.
In the storyboard, notice how there's a Relationship that connects the "window content" to the ViewController. So, the ViewController can now do things that you might previously have done in a window controller.
The ViewController.swift file will already have a default override of viewDidLoad() that will be called when the window loads, just as you were expecting windowDidLoad() to be called if it were an NSWindowController subclass.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
println("Executed")
}