Cocoa OS X - Change background of window in Swift - swift

I am making an app for OS X with Cocoa with Xcode and I want to change the Windows background color with alpha in swift. I don't know how to call the window from the code and how to set its background color with alpha value. How can I do that?
Here is my app delegate class, with hierarchy:

Perhaps the AppDelegate is not the right place to add it if you are using storyboards. There is an easy way by modifying the view's layer:
In Interface Builder, select the child view that is held by the view controller of the Window.
Go to the the view's Effects Inspector and enable Core Animation Layer.
Add this line of code in your viewDidLoad() method (viewcontroller.swift):
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.wantsLayer = true;
self.view.layer?.backgroundColor = NSColor.whiteColor().CGColor
}

Thanks for the code and pictures to the Core Animation. That solved most of my problems. I then found that because more was going on later with windows that I needed to move your code to viewWillAppear. It has been much more predictable there. I got this information from another question on SO and thought I would update this thread as well since it was my starting point on the problem.
The following goes in viewController.swift after you have enabled Core Animation Layer for the View in the Effects Inspector.
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
self.view.layer?.backgroundColor = NSColor.redColor().CGColor
}

Related

Swift: View position resets when app is minimized, then brough back to foreground

I have a UIView that starts off in a random position (set up through storyboard), then finds its actual position when viewDidLoad() is called, depending on the user preferences stored in UserDefaults.
The problem is when the app is minimized, and then brought back to the foreground, the position of that UIView resets to the initial random position.
How could I prevent the app from releasing that position determined at runtime? Or do I need to re-calculate that position through a function that is triggered when the app is brought back from being inactive?
I don't think there is much use to include code for this, but the position of the UIView (named 'contextUnderscoreLine') is set in viewDidLoad() with contextUnderscoreLine.center = savedCGPoint.
I think I found a solution for this, in case someone else was going through the same issue:
Add an observer in viewDidLoad() to detect when the application is 'active'. From documentation, my understanding is that this will get called once when the app is launched, and then every time the app comes back from being in the background. So the initial formatting of the problematic UIView can be done there instead of in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc func applicationDidBecomeActive(notification: NSNotification) {
// Application is back in the foreground
//determine the position of the UIView here, instead of in viewDidLoad()
}
Edit
For my specific case it was better to use UIApplication.willEnterForegroundNotification instead of UIApplication.didBecomeActiveNotification. That way the changes in UIView are done before the Application is back in the foreground, and therefore you avoid the noticeable UIView jitters as the changes are made in front of the user.

Swift: SKSpriteKit, using Storyboards, UIViewController and UIButton to set in game parameters?

Context
The default code that comes with a new SpriteKit game has a storyboard such that - following the launch screen - all there is, is a GameViewController which calls forth the GameScene. However, this may be less ideal for many games. For example, one may wish to have the user select difficulty from a main menu, and then go to the GameScene - outlined below:
Notably, the middle view controller is a custom class MyUIViewController so that the UIButtons "easy" and "hard" can have the following IBActions:
#IBAction func setGameDifficultyToEasy(sender: AnyObject) {
gameDifficulty = "easy"
print("Game difficulty set to \(gameDifficulty)")
}
#IBAction func setGameDifficultyToHard(sender: AnyObject) {
gameDifficulty = "hard"
print("Game difficulty set to \(gameDifficulty)")
}
where gameDifficulty is a global variable, that the GameScene utilizes to determine aspects of the game play.
In the M.W.E. setting gameDifficulty to "hard" causes there to be three sprites on the screen, whereas setting gameDifficulty to "easy" puts forth only two.
Easy
Hard
Question
In the following gif, one sees that:
gameDifficuly is initialized as "hard"
the UIButton "Easy" was selected.
This can be seen be the printout statements.
Interestingly, although the UIButton was pressed first then the GameViewController was called, the changing of the parameter gameDifficulty was not set until after the GameScene was rendered.
**How can I get the UIButtons to set the parameter gameDifficulty prior to GameScene being called?
Minimum Working Example
Stack Overflow SKSpriteKit
Note
My answer here shows that this is clearly possible, but by relegating everything to SKViews rather than using a storyboard. So if possible, please keep answers related to using storyboards.

SWRevealViewController conflict with UISlider

I currently facing an issue trying to delegate SWRevealViewController panGestureRecognizer method in one of my view.
When i slide my UISlider, the panGesture interfer and open the sidemenu instead to move my slider.
i tried to delegate the panGesture and it works well, but if i quit my view and go to an other, the pangesture is not functionnal anymore, and i can't reveal my sidemenu from my second view.
My code :
class Search : UIViewController, UIGestureRecognizerDelegate{
#IBOutlet weak var sliderprice: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
self.revealViewController().panGestureRecognizer().delegate = self
}
override func viewDidAppear(animated: Bool) {
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if (touch.view == self.sliderprice){
return false
}
else{
return true
}
}
}
While this is an old question, I'll answer it anyways, maybe for someone coming from Google search it will be helpful.
After many hours of research and thinking, finally I was able to come up with multiple solutions to this problem.
The Lazy
This solution is not recommended, but working, so I decided to list it.
Before setting self as self.revealViewController().panGestureRecognizer()'s delegate, store the original self.revealViewController().panGestureRecognizer().delegate to a property, and when you leave the screen at viewWillDisappear(), set self.revealViewController().panGestureRecognizer().delegate back to the one you stored in viewDidLoad(). So in the end it gets back its original delegate. Tampering with delegates like this is never really recommended, but I said, it works.
The Nicer One
I consider this still not the best solution, but we are getting there. Find a class, a controller that you use in the whole application and gets called when you start the app. Here set the SWRevealViewController's panGestureDelegate to this class, and overwrite the gestureRecognizerShouldBeginmethod appropriately (see below).
The Best One - (in my opinion)
Now this is the best and most clear solution.
For the time being (May, 2018) the last commit to SWRevealViewController was in 2015.
Fork the original project ( https://github.com/John-Lluch/SWRevealViewController ) or simply copy the two necessary files (SWRevealViewController.m and SWRevealViewController.h) and place them into a separate folder to handle 3rd party libraries. Then you can remove SWRevealViewController from your Podfile. Don't forget to fix your imports for SWRevealViewController where you have it.
Now you are free to modify the files. What I suggest is the following.
Go to SWRevealViewController.m and implement the following method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
return ![touch.view isKindOfClass:UISlider.class];
}
If the touched view is a UISlider (or a custom class that inherits from UISlider) the gesture won't begin, meaning the pan gesture will no longer be conflicted with the UISlider's pan gesture.
I seriously hope I could help anyone out there as this problem was a pain in my back for quite some time now.

Start the animation of something when app starts

I am trying to learn Swift. I currently have a button that plays an animation but I want to have the animation start immediately after the app loads. How would I go about doing that?
You need to put your animation on the viewDidApear method, while will be called after your viewDidLoad method.
For example
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
UIView.animateWithDuration(2, animations: {
//do what ever here
})
}
Your question is not very specific so I'm not sure if this answers your question.
When your iOS application starts, there is usually a View Controller that gets rendered on the screen as your main entry point. This can be changed in in your AppDelegate.swift
Now, let's assume that in your application, a view controller named WelcomeViewController is the first page and you have your button along with the rest of your logic in it. Every view controller has certain functions that you can override and use. For example:
viewDidLoad
viewDidAppear
viewWillAppear
viewWillDisappear
the names are self-explanatory so I'm sure you can guess what they do. In your case, just override viewDidAppear and start your animation in there.
override func viewDidAppear(animated: Bool) {
...
}

Keep window always on top?

In Objective-C for Cocoa Apps it's possible to use such way to keep window always on top?
How to achieve the same with Swift?
self.view.window?.level = NSFloatingWindowLevel
Causes build error Use of unresolved identifier 'NSFloatingWindowLevel'
To change the window level you can't do it inside viewDidload because view's window property will always be nil there but it can be done overriding viewDidAppear method or any other method that runs after view is installed in a window (do not use it inside viewDidLoad):
Swift 4 or later
override func viewDidAppear() {
super.viewDidAppear()
view.window?.level = .floating
}
For older Swift syntax check the edit history
I would prefer this way. This ignores all other active apps, and makes your app upfront.
override func viewWillAppear() {
NSApplication.sharedApplication().activateIgnoringOtherApps(true)
}
While the other answers are technically correct - when your app will or did resigns active, setting the window level to .floating will have no effect.
.floating means on top of all the windows from the app you are working on, it means not on top of all apps windows.
Yes there are other levels available you could set, like kCGDesktopWindowLevel which you can and should not set in swift to make your window float above all.
None of them will change the fact that your window will go behind the focused and active apps window. To circumvent i chose to observe if the app resigns active notification and act accordingly.
var observer : Any;
override func viewDidLoad() {
super.viewDidLoad()
observer = NotificationCenter.default.addObserver(
forName: NSApplication.didResignActiveNotification,
object: nil,
queue: OperationQueue.main ) { (note) in
self.view.window?.level = .floating;
// you can also make your users hate you, to take care, don't use them.
//NSApplication.shared.activate(ignoringOtherApps: true)
//self.view.window?.orderFrontRegardless();
}
}
another way could be subclassing NSWindow and override the property .level with an always returning .floating, but the code above is less work and keeps control in the place where you want to set the window floating.
I spent a long time trying to make this work. I then realized there was a simple answer, just worded in a different way. Here it is: Change macOS window level with SwiftUI (to make a floating window)
As explained there:
You can access your windows with NSApplication.shared.windows and set the level for each one.
for window in NSApplication.shared.windows {
window.level = .floating
}
EDIT: you can use other levels, including .screenSaver (highest, I think) and ```.normal`` if you want to return to standard behavior. Source: https://developer.apple.com/documentation/appkit/nswindow/level