NSView and Swift 3.0 - nsview

I have finally upgraded to Swift 3.0 and encountered a problem - I'm using custom view and its drawrect function to render some drawing, and I override 'isFlipped' var to return true and draw them upside down. Before Swift 3.0 it works like a charm but now Cocoa turns upside down not only this particular drawing but also any other my subviews (which I don't want to have this way). Does anyone know how to fix this?

I didn't figure out how to return back 'isFlipped' var behavior from Swift 2 so instead I simply rotate coordinates of all my subviews. It could be done like this:
override func viewWillAppear() {
for child in self.view.subviews {
child.frame = NSRect.init(
x: child.frame.minX,
y: self.view.frame.height - child.frame.minY - child.frame.height,
width: child.frame.width,
height: child.frame.height)
}
}

Related

NSWindow transition animation for View Controller Segues

I've been trying to figure out how to get a NSWindow to perform a transition animation with Swift 3. I found a few examples in Objective-C, but I haven't been able to tease out the relevant details and translate into the target language / newer SDK and get it applied to the right object. This one is pretty flipping cool, but it's ~8yrs old: http://www.theregister.co.uk/2009/08/21/cocoa_graphics_framework/ -- I would imagine there's a better way to do the CGSCube effect now in macOS Sierra with Swift.
Here's what I have so far:
class ViewController: NSViewController {
func doAnimation() {
if let layer = view .layer {
layer.backgroundColor = CGColor.black
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(CGFloat.pi * 2.0)
rotateAnimation.duration = 10.0
layer.add(rotateAnimation, forKey: nil)
}
}
override func viewWillAppear() {
if let window = self.view.window {
window.styleMask = NSWindowStyleMask.borderless
window.backingType = NSBackingStoreType.buffered
window.backgroundColor = NSColor.clear
window.isOpaque = false
window.hasShadow = false
}
}
override func viewDidLoad() {
super.viewDidLoad()
doAnimation()
}
}
This doesn't really do the trick at all. I get a white background on my window instead of a transparent background, and my view rolls across the frame instead of the window frame itself being animated.
Ideally, I would like to do something more advanced like these 3d transitions -- https://cocoapods.org/pods/MKCubeController & https://www.appcoda.com/custom-view-controller-transitions-tutorial/ & https://github.com/andresbrun/ABCustomUINavigationController#cube but I'm not quite sure how to translate the examples from the iOS SDK over to the macOS SDK without UIKit. (Annoyingly, I remember pulling this off a few years back in ObjC, but the project was lost somewhere between formats / new computers.)
How can I apply a transform to the NSWindow itself while segueing between View Controllers? Any tips toward adding some 3d to this effect would be appreciated. I hoping there's maybe a CocoaPod that gets me halfway there.

Adjust NSVisualEffectView blur radius and transparency

Is it possible to adjust the blur radius and transparency of an NSVisualEffectView when it's applied to an NSWindow (Swift or Objective-C)? I tried all variations of NSVisualEffectMaterial (dark, medium, light) - but that's not cutting it. In the image below I've used Apple's non-public API with CGSSetWindowBackgroundBlurRadius on the left, and NSVisualEffectView on the right.
I'm trying to achieve the look of what's on the left, but it seems I'm relegated to use the methods of the right.
Here's my code:
blurView.blendingMode = NSVisualEffectBlendingMode.BehindWindow
blurView.material = NSVisualEffectMaterial.Medium
blurView.state = NSVisualEffectState.Active
self.window!.contentView!.addSubview(blurView)
Possibly, related - but doesn't answer my question:
OS X NSVisualEffect decrease blur radius? - no answer
Although I wouldn't recommend this unless you are ready to fall back to it not working in a future release, you can subclass NSVisualEffectView with the following to do what you want:
- (void)updateLayer
{
[super updateLayer];
[CATransaction begin];
[CATransaction setDisableActions:YES];
CALayer *backdropLayer = self.layer.sublayers.firstObject;
if ([backdropLayer.name hasPrefix:#"kCUIVariantMac"]) {
for (CALayer *activeLayer in backdropLayer.sublayers) {
if ([activeLayer.name isEqualToString:#"Active"]) {
for (CALayer *sublayer in activeLayer.sublayers) {
if ([sublayer.name isEqualToString:#"Backdrop"]) {
for (id filter in sublayer.filters) {
if ([filter respondsToSelector:#selector(name)] && [[filter name] isEqualToString:#"blur"]) {
if ([filter respondsToSelector:#selector(setValue:forKey:)]) {
[filter setValue:#5 forKey:#"inputRadius"];
}
}
}
}
}
}
}
}
[CATransaction commit];
}
Although this doesn't use Private APIs per se, it does start to dig into layer hierarchies which you do not own, so be sure to double check that what you are getting back is what you expect, and fail gracefully if not. For instance, on 10.10 Yosemite, the Backdrop layer was a direct decedent of the Visual Effect view, so things are likely to change in the future.
I had the same issue as you had and I have solved it with a little trick seems to do the job that I wanted. I hope that it will also help you.
So in my case, I have added the NSVisualEffectView in storyboards and set its properties as follows:
and my View hierarchy is as follows:
All that reduces the blur is in the NSViewController in:
override func viewWillAppear() {
super.viewWillAppear()
//Adds transparency to the app
view.window?.isOpaque = false
view.window?.alphaValue = 0.98 //tweak the alphaValue for your desired effect
}
Going with your example this code should work in addition with the tweak above:
let blurView = NSVisualEffectView(frame: view.bounds)
blurView.blendingMode = .behindWindow
blurView.material = .fullScreenUI
blurView.state = .active
view.window?.contentView?.addSubview(blurView)
Swift 5 code
For anyone interested here is a link to my repo where I have created a small prank app which uses the code above:
GitHub link

Animation in core graphics

I followed this awesome Rey Wenderlich tutorial to make an Bezier arc and increment/decrement values. But how to animate the arch instead of just step-up and step-down?
http://www.raywenderlich.com/90690/modern-core-graphics-with-swift-part-1
I tried putting animation block in custom property declaration, which I dont think is the right place to do it and xcode doesn't let me do it anyway.
#IBInspectable var counter: Int = 5 {
didSet {
if counter <= NoOfGlasses {
//the view needs to be refreshed
UIView.animateWithDuration(0.2, animations: {
setNeedsDisplay()
}, completion:nil
)
}
}
}
Also tried to put the increment in animation block in View controller, didn't work.
#IBAction func btnPushButton(sender: AnyObject) {
UIView.animateWithDuration(0.2, animations: {
self.arcView.counter = self.arcView.counter + 10
self.counterLabel.text = String(self.arcView.counter)
}, completion:nil
)
}
Are you describing this sort of thing?
That's a simpler example - it's just a drawn triangle - but it's the same idea, if I'm understanding you correctly: we are animating the difference between one drawing and another.
Basically you have two choices. The easy way is to use CAShapeLayer, which animates for you automatically when you change its path. The other choice is to do what I'm doing here, which is to create a custom animatable property - in this case, a property representing the x-position of the bottom point of the triangle.

How can I make a vertical slider in swift?

I was wondering how I can create a vertical slider in swift.
I have tried using .transform and then adding a rotate but this returned an exception and rotated the whole screen. I just need a vertical slider.
I was wondering if anyone know of a way to rotate a slider in swift?
Thanks
Assuming you are talking about UISlider :
You were looking in a right direction but probably applied AffineTransformation to the wrong item... You should use UISlider.transform to change it orientation.
Here is working code (I did add UISlider using InterfaceBuilder):
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var verticalSlider: UISlider!{
didSet{
verticalSlider.transform = CGAffineTransform(rotationAngle: CGFloat(-M_PI_2))
}
}
}
Options 1:
In Swift 4:
-M_PI_2 is deprecated so Use Double.pi / 2
verticalSlider.transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi / 2))
OR
Options 2: for All Objective C and Swift
Key path = layer.transform.rotation.z
Type = String
Value = -1.570795
Most Imp Note: before and After keypath No Space
Use OOP. If you define this rotation as an Extension on UIView, then not only the UIView but also all subclasses that inherit from UIView can be rotated. This of course includes UISlider, but also UILabel, UIButton and about 200? other subclasses... see here
list of subclasses for UIView
extension UIView
{
func makeVertical()
{
transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
}
}
// usage example:
sldBoilerTemperature.makeVertical()
It's good to be aware of inheritance, saves a lot of work. For many Swift extensions to make, it might be a good idea trying to move it upwards in the class hierarchy, like in this example.
Swift-5 Vertical slider with constraints
func transformSlider(){
var constraints = [NSLayoutConstraint]()
ControlSlider.transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi/2))
ControlSlider.translatesAutoresizingMaskIntoConstraints = false
constraints.append(ControlSlider.widthAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.8))
constraints.append(ControlSlider.centerYAnchor.constraint(equalTo: self.centerYAnchor))
let sliderWidth = (ControlSlider.frame.height * 0.8)/2
print(sliderWidth)
constraints.append(ControlSlider.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: (20-sliderWidth)))
NSLayoutConstraint.activate(constraints)
}

Swift : CGRect interactive?

Hi good fellows of the Code.
I'm still trying to figure out how Swift's working, and how to use it to make nice interactive UI.
Therefore, I'm trying to make an CGRect being draggable, touch-sensible, etc.
I've seen that's it's quite easy to do so with images in the storyboard, but in that case it's not working because the CGRect does not appears in the storyboard as it is generated by the code.
So far, my code is :
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
var square: UIView!
override func viewDidLoad() {
super.viewDidLoad()
square = UIView(frame: CGRect(x: 200, y: 200, width: 100, height: 100))
square.backgroundColor = UIColor.grayColor()
square.userInteractionEnabled = true
view.addSubview(square)
}
}
It makes a very nice gray square.
But I can't figure how to make it move. I tried several things, like adding the working code of an IBAction like this :
func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
recognizer.view.center = CGPoint(x:recognizer.view.center.x + translation.x,
y:recognizer.view.center.y + translation.y)
recognizer.setTranslation(CGPointZero, inView: self.view)
}
But it's returning an Thread 1 SIGBRT error.
I tried replacing self.view by square, it's not better.
A link with the error : http://imgur.com/SAmOOXv
If someone here can either show me an example or a link related to that topic I would be extremely happy.
Thanks for the help !
EDIT : To be clear, I tried to work it as a classic IBAction, which means I tried to find a way to call the handlePan function. But either it's not how it's intended to work, or I'm just not doing it right.