Swift property observer issue - swift

I've an issue with my property observer and would like to know a little bit more about the Swift behaviour.
I've the following architecture which use callbacks:
A higher lever class
class MyFirstClass : NSScrollView {
var colorDidUpdate: (() -> ())?
var color = NSColor.black {
didSet {
colorDidUpdate?()
}
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
/* Some init */
var mySecondClass = MySecondClass(frame: frame)
colorDidUpdate = mySecondClass.enclosingViewDidUpdateColor
mySecondClass.color = color
documentView = mySecondClass
/* Some other init */
}
}
Then a second class that act like a link between the first and third class
class MySecondClass {
var viewColorDidUpdate: (() -> ())?
var color: NSColor?
var myThirdClass = MyThirdClass(frame: frame)
init(frame frameRect: NSRect) {
/* Some init */
viewColorDidUpdate = myThirdClass.updateColor
myThirdClass.color = color
/* Some other init */
}
func enclosingViewDidUpdateColor() {
viewStyleDidUpdate?()
}
}
And finally a third class where the draw is done.
class MyThirdClass {
var color: NSColor?
func draw(_ dirtyRect: NSRect) {
guard let color = color else { return }
// The color is black instead of green.
}
/* proerties and functions... */
func updateColor() {
functionThatWillTriggerTheDrawFunction() // Will trigger draw function
}
}
If I set a new color to MyClass property like
var myClass = MyClass()
myClass.color = .green
The printed color is not "green" but it still black...
I thought that when we were in the didSet scope the variable was already set, am I wrong?
Should I use an other pattern?

I thought that when we were in the didSet scope the variable was already set, am I wrong?
No that's true, but something else is happening I guess (although the relevant code doesn't seem to be included)
A closure as the name suggests closes over variables it uses at the time of its definition. So you are most likely defining/using the closure at the time where your color still is black. You can not use a closure to give you the current value of a variable that is captured but you could pass the value into the closure as a parameter. I hope this makes sense.
If you provide a more complete code sample I can get a better idea about what the problem in your case might be.
Update (after you provided more code):
You are only ever setting the colors of the second and third classes on their init methods. You are never updating their color properties when you update the first classes color property.
I am sure the simplification of your presented code is partly to blame, but there are a few things you might want to consider to make things easier to follow:
Try not saving the color in each and every component separately but rather pass it along as parameters of your functions/methods. This makes it easier to see, what is happening. For example, you could call your change handler colorDidUpdate(to color: NSColor) and pass in the new value. This way, at least your second class doesn't need to store the color but rather pass it along into updateColor(_ color: NSColor) which could set the third class' color property and trigger a redraw.
In general I think it is beneficial to pass in any change to a change handler and not read it from a global state. Try not to store everything but pass along the information you need without storing it in between (if possible). This makes it easier to see where and how the data and information flows in your app and might indicate problems with the architecture.
It is a bit hard suggesting to architect the entire thing differently since the code is just fragments here, but it looks like you could improve and simplify a bit.

Related

How, exactly, do I render Metal on a background thread?

This problem is caused by user interface interactions such as showing the titlebar while in fullsreen. That question's answer provides a solution, but not how to implement that solution.
The solution is to render on a background thread. The issue is, the code provided in Apple's is made to cover a lot of content so most of it will extraneous code, so even if I could understand it, it isn't feasible to use Apple's code. And I can't understand it so it just plain isn't an option. How would I make a simple Swift Metal game use a background thread being as concise as possible?
Take this, for example:
class ViewController: NSViewController {
var MetalView: MTKView {
return view as! MTKView
}
var Device: MTLDevice = MTLCreateSystemDefaultDevice()!
override func viewDidLoad() {
super.viewDidLoad()
MetalView.delegate = self
MetalView.device = Device
MetalView.colorPixelFormat = .bgra8Unorm_srgb
Device = MetalView.device
//setup code
}
}
extension ViewController: MTKViewDelegate {
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
//drawing code
}
}
That is the start of a basic Metal game. What would that code look like, if it were rendering on a background thread?
To fix that bug when showing the titlebar in Metal, I need to render it on a background thread. Well, how do I render it on a background thread?
I've noticed this answer suggests to manually redraw it 60 times a second. Presumably using a loop that is on a background thread? But that seems... not a clean way to fix it. Is there a cleaner way?
The main trick in getting this to work seems to be setting up the CVDisplayLink. This is awkward in Swift, but doable. After some work I was able to modify the "Game" template in Xcode to use a custom view backed by CAMetalLayer instead of MTKView, and a CVDisplayLink to render in the background, as suggested in the sample code you linked — see below.
Edit Oct 22:
The approach mentioned in this thread seems to work just fine: still using an MTKView, but drawing it manually from the display link callback. Specifically I was able to follow these steps:
Create a new macOS Game project in Xcode.
Modify GameViewController to add a CVDisplayLink, similar to below (see this question for more on using CVDisplayLink from Swift). Start the display link in viewWillAppear and stop it in viewWillDisappear.
Set mtkView.isPaused = true in viewDidLoad to disable automatic rendering, and instead explicitly call mtkView.draw() from the display link callback.
The full content of my modified GameViewController.swift is available here.
I didn't review the Renderer class for thread safety, so I can't be sure no more changes are required, but this should get you up and running.
Older implementation with CAMetalLayer instead of MTKView:
This is just a proof of concept and I can't guarantee it's the best way to do everything. You might find these articles helpful too:
I didn't try this idea, but given how much convenience MTKView generally provides over CAMetalLayer, it might be worth giving it a shot:
https://developer.apple.com/forums/thread/89241?answerId=268384022#268384022
Is drawing to an MTKView or CAMetalLayer required to take place on the main thread? and https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction
class MyMetalView: NSView {
var displayLink: CVDisplayLink?
var metalLayer: CAMetalLayer!
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
setupMetalLayer()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupMetalLayer()
}
override func makeBackingLayer() -> CALayer {
return CAMetalLayer()
}
func setupMetalLayer() {
wantsLayer = true
metalLayer = layer as! CAMetalLayer?
metalLayer.device = MTLCreateSystemDefaultDevice()!
// ...other configuration of the metalLayer...
}
// handle display link callback at 60fps
static let _outputCallback: CVDisplayLinkOutputCallback = { (displayLink, inNow, inOutputTime, flagsIn, flagsOut, context) -> CVReturn in
// convert opaque context pointer back into a reference to our view
let view = Unmanaged<MyMetalView>.fromOpaque(context!).takeUnretainedValue()
/*** render something into view.metalLayer here! ***/
return kCVReturnSuccess
}
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
guard CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) == kCVReturnSuccess,
let displayLink = displayLink
else {
fatalError("unable to create display link")
}
// pass a reference to this view as an opaque pointer
guard CVDisplayLinkSetOutputCallback(displayLink, MyMetalView._outputCallback, Unmanaged<MyMetalView>.passUnretained(self).toOpaque()) == kCVReturnSuccess else {
fatalError("unable to configure output callback")
}
guard CVDisplayLinkStart(displayLink) == kCVReturnSuccess else {
fatalError("unable to start display link")
}
}
deinit {
if let displayLink = displayLink {
CVDisplayLinkStop(displayLink)
}
}
}

Remove SKAction and restore node state

Desired behavior is: when an action is removed from a node (with removeAction(forKey:) for instance) it stops to animate and all the changes caused by action are discarded, so the node returns back to pervious state. In other words, I want to achieve behavior similar to CAAnimation.
But when a SKAction is removed, the node remains changed. It's not good, because to restore it's state I need to know exactly what action was removed. And if I then change the action, I also will need to update the node state restoration.
Update:
The particular purpose is to show possible move in a match-3 game. When I show a move, pieces start pulsating (scale action, repeating forever). And when the user moves I want to stop showing the move, so I remove the action. As the result, pieces may remain downscaled. Later I would like to add more fancy and complicated animations, so I want to be able to edit it easily.
Thanks to the helpful comment and answer I came to my own solution. I think the state machine would be bit too heavy here. Instead I created a wrapper node, which main purpose is run the animation. It also has a state: isAimating property. But, first of all, it allows to keep startAnimating() and stopAnimating() methods close to each other, incapsulated, so it's more difficult to mess up.
class ShowMoveAnimNode: SKNode {
let animKey = "showMove"
var isAnimating: Bool = false {
didSet {
guard oldValue != isAnimating else { return }
if isAnimating {
startAnimating()
} else {
stopAnimating()
}
}
}
private func startAnimating() {
let shortPeriod = 0.2
let scaleDown = SKAction.scale(by: 0.75, duration: shortPeriod)
let seq = SKAction.sequence([scaleDown,
scaleDown.reversed(),
scaleDown,
scaleDown.reversed(),
SKAction.wait(forDuration: shortPeriod * 6)])
let repeated = SKAction.repeatForever(seq)
run(repeated, withKey: animKey)
}
private func stopAnimating() {
removeAction(forKey: animKey)
xScale = 1
yScale = 1
}
}
Usage: just add everything that should be animated to this node. Works well with simple animations, like: fade, scale and move.
As #Knight0fDragon suggested, you would be better off using the GKStateMachine functionality, I will give you an example.
First declare the states of your player/character in your scene
lazy var playerState: GKStateMachine = GKStateMachine(states: [
Idle(scene: self),
Run(scene: self)
])
Then you need to create a class for each of these states, in this example I will show you only the Idle class
import SpriteKit
import GameplayKit
class Idle: GKState {
weak var scene: GameScene?
init(scene: SKScene) {
self.scene = scene as? GameScene
super.init()
}
override func didEnter(from previousState: GKState?) {
//Here you can make changes to your character when it enters this state, for example, change his texture.
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass is Run.Type //This is pretty obvious by the method name, which states can the character go to from this state.
}
override func update(deltaTime seconds: TimeInterval) {
//Here is the update method for this state, lets say you have a button which controls your character velocity, then you can check if the player go over a certain velocity you make it go to the Run state.
if playerVelocity > 500 { //playerVelocity is just an example of a variable to check the player velocity.
scene?.playerState.enter(Run.self)
}
}
}
Now of course in your scene you need to do two things, first is initialize the character to a certain state or else it will remain stateless, so you can to this in the didMove method.
override func didMove(to view: SKView) {
playerState.enter(Idle.self)
}
And last but no least is make sure the scene update method calls the state update method.
override func update(_ currentTime: TimeInterval) {
playerState.update(deltaTime: currentTime)
}

Swift IBInspectable didSet versus get/set

I am relatively new to IBDesignables and IBInspectable's and I noticed that a lot of tutorial use IBInspectable in this fashion.
#IBInspectable var buttonBorderWidth: CGFloat = 1.0 {
didSet {
updateView()
}
}
func updateView() {
// Usually there are more entries here for each IBInspectable
self.layer.borderWidth = buttonBorderWidth
}
But in some instances they use get and set like this for example
#IBInspectable
var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
Can someone please explain: What is happening in each of these cases and how to choose which one to use?
I see two questions. The first one is “What is happening in each of these cases”, and is best answered by reading the “Properties” chapter of The Swift Programming Language. There are also already three other answers posted which address the first question, but none of them answer the second, and more interesting, question.
The second question is “how to choose which one to use”.
Your shadowOpacity example (which is a computed property) has the following advantages over your buttonBorderWidth example (which is a stored property with an observer):
All of the shadowOpacity-related code is in one place, so it's easier to understand how it works. The buttonBorderWidth code is spread between didSet and updateViews. In a real program these functions are more likely to be farther apart, and as you said, “Usually there are more entries here for each IBInspectable”. This makes it harder to find and understand all the code involved in implementing buttonBorderWidth.
Since the view's shadowOpacity property getter and setter just forward to the layer's property, the view's property doesn't take any additional space in the view's memory layout. The view's buttonBorderWidth, being a stored property, does take additional space in the view's memory layout.
There is an advantage to the separate updateViews here, but it is subtle. Notice that buttonBorderWidth has a default value of 1.0. This is different than the default value of layer.borderWidth, which is 0. Somehow we need to get layer.borderWidth to match buttonBorderWidth when the view is initialized, even if buttonBorderWidth is never modified. Since the code that sets layer.borderWidth is in updateViews, we can just make sure we call updateViews at some point before the view is displayed (e.g. in init or in layoutSubviews or in willMove(toWindow:)).
If we want to make buttonBorderWidth be a computed property instead, we either have to force-set the buttonBorderWidth to its existing value somewhere, or duplicate the code that sets layer.borderWidth somewhere. That is, we either have to do something like this:
init(frame: CGRect) {
...
super.init(frame: frame)
// This is cumbersome because:
// - init won't call buttonBorderWidth.didSet by default.
// - You can't assign a property to itself, e.g. `a = a` is banned.
// - Without the semicolon, the closure is treated as a trailing
// closure on the above call to super.init().
;{ buttonBorderWidth = { buttonBorderWidth }() }()
}
Or we have to do something like this:
init(frame: CGRect) {
...
super.init(frame: frame)
// This is the same code as in buttonBorderWidth.didSet:
layer.borderWidth = buttonBorderWidth
}
And if we have a bunch of these properties that cover layer properties but have different default values, we have to do this force-setting or duplicating for each of them.
My solution to this is generally to not have a different default value for my inspectable property than for the property it covers. If we just let the default value of buttonBorderWidth be 0 (same as the default for layer.borderWidth), then we don't have to get the two properties in sync because they're never out-of-sync. So I would just implement buttonBorderWidth like this:
#IBInspectable var buttonBorderWidth: CGFloat {
get { return layer.borderWidth }
set { layer.borderWidth = newValue }
}
So, when would you want to use a stored property with an observer? One condition especially applicable to IBInspectable is when the inspectable properties do not map trivially onto existing layer properties.
For example, in iOS 11 and macOS 10.13 and later, CALayer has a maskedCorners property that controls which corners are rounded by cornerRadius. Suppose we want to expose both cornerRadius and maskedCorners as inspectable properties. We might as well just expose cornerRadius using a computed property:
#IBInspectable var cornerRadius: CGFloat {
get { return layer.cornerRadius }
set { layer.cornerRadius = newValue }
}
But maskedCorners is essentially four different boolean properties combined into one. So we should expose it as four separate inspectable properties. If we use computed properties, it looks like this:
#IBInspectable var isTopLeftCornerRounded: Bool {
get { return layer.maskedCorners.contains(.layerMinXMinYCorner) }
set {
if newValue { layer.maskedCorners.insert(.layerMinXMinYCorner) }
else { layer.maskedCorners.remove(.layerMinXMinYCorner) }
}
}
#IBInspectable var isBottomLeftCornerRounded: Bool {
get { return layer.maskedCorners.contains(.layerMinXMaxYCorner) }
set {
if newValue { layer.maskedCorners.insert(.layerMinXMaxYCorner) }
else { layer.maskedCorners.remove(.layerMinXMaxYCorner) }
}
}
#IBInspectable var isTopRightCornerRounded: Bool {
get { return layer.maskedCorners.contains(.layerMaxXMinYCorner) }
set {
if newValue { layer.maskedCorners.insert(.layerMaxXMinYCorner) }
else { layer.maskedCorners.remove(.layerMaxXMinYCorner) }
}
}
#IBInspectable var isBottomRightCornerRounded: Bool {
get { return layer.maskedCorners.contains(.layerMaxXMaxYCorner) }
set {
if newValue { layer.maskedCorners.insert(.layerMaxXMaxYCorner) }
else { layer.maskedCorners.remove(.layerMaxXMaxYCorner) }
}
}
That's a bunch of repetitive code. It's easy to miss something if you write it using copy and paste. (I don't guarantee that I got it correct!) Now let's see what it looks like using stored properties with observers:
#IBInspectable var isTopLeftCornerRounded = true {
didSet { updateMaskedCorners() }
}
#IBInspectable var isBottomLeftCornerRounded = true {
didSet { updateMaskedCorners() }
}
#IBInspectable var isTopRightCornerRounded = true {
didSet { updateMaskedCorners() }
}
#IBInspectable var isBottomRightCornerRounded = true {
didSet { updateMaskedCorners() }
}
private func updateMaskedCorners() {
var mask: CACornerMask = []
if isTopLeftCornerRounded { mask.insert(.layerMinXMinYCorner) }
if isBottomLeftCornerRounded { mask.insert(.layerMinXMaxYCorner) }
if isTopRightCornerRounded { mask.insert(.layerMaxXMinYCorner) }
if isBottomRightCornerRounded { mask.insert(.layerMaxXMaxYCorner) }
layer.maskedCorners = mask
}
I think this version with stored properties has several advantages over the version with computed properties:
The parts of the code that are repeated are much shorter.
Each mask option is only mentioned once, so it's easier to make sure the options are all correct.
All the code that actually computes the mask is in one place.
The mask is constructed entirely from scratch each time, so you don't have to know the mask's prior value to understand what its new value will be.
Here's another example where I'd use a stored property: suppose you want to make a PolygonView and make the number of sides be inspectable. We need code to create the path given the number of sides, so here it is:
extension CGPath {
static func polygon(in rect: CGRect, withSideCount sideCount: Int) -> CGPath {
let path = CGMutablePath()
guard sideCount >= 3 else {
return path
}
// It's easiest to compute the vertices of a polygon inscribed in the unit circle.
// So I'll do that, and use this transform to inscribe the polygon in `rect` instead.
let transform = CGAffineTransform.identity
.translatedBy(x: rect.minX, y: rect.minY) // translate to the rect's origin
.scaledBy(x: rect.width, y: rect.height) // scale up to the rect's size
.scaledBy(x: 0.5, y: 0.5) // unit circle fills a 2x2 box but we want a 1x1 box
.translatedBy(x: 1, y: 1) // lower left of unit circle's box is at (-1, -1) but we want it at (0, 0)
path.move(to: CGPoint(x: 1, y: 0), transform: transform)
for i in 1 ..< sideCount {
let angle = CGFloat(i) / CGFloat(sideCount) * 2 * CGFloat.pi
print("\(i) \(angle)")
path.addLine(to: CGPoint(x: cos(angle), y: sin(angle)), transform: transform)
}
path.closeSubpath()
print("rect=\(rect) path=\(path.boundingBox)")
return path
}
}
We could write code that takes a CGPath and counts the number of segments it draws, but it is simpler to just store the number of sides directly. So in this case, it makes sense to use a stored property with an observer that triggers an update to the layer path:
class PolygonView: UIView {
override class var layerClass: AnyClass { return CAShapeLayer.self }
#IBInspectable var sideCount: Int = 3 {
didSet {
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
(layer as! CAShapeLayer).path = CGPath.polygon(in: bounds, withSideCount: sideCount)
}
}
I update the path in layoutSubviews because I also need to update the path if the view's size changes, and a size change also triggers layoutSubviews.
First of all, what you are asking about is nothing to do with #IBInspectable or #IBDesignable. Those are just directives for XCode to use with the Interface Builder when you create your own View/ViewControllers. Any property with #IBInspectable also appears in the attributes inspector in the Interface Builder. And #IBDesignable is for displaying the custom view in Interface builder. Now to get to the didSet and get/set
didSet
This is what you call a Property Observer. You can define property observers for a stored property to monitor the changes in a property. There are 2 flavors to monitor the change willSet and didSetthat can be defined. So you define the observers to perform some block of code where there is a change to that property. If you define willSet that code will be called before the property is set. Likewise didSet is the block run after the property has been set. So depending on what you need to do you can implement either of the observers.
get/set
Besides stored properties you can define something called Computed properties. As the name implies computed properties do not create and store any values themselves. These values are computed when needed. So these properties need get and set code to compute the property when required. If there is only a get that means it’s a read only property.
Hope this helps. Read the Swift book and go through the first few lectures of CS193p on iTunesU
didSet means "do the following when the variable is set". In your case, if you change buttonBorderWidth, the function updateView() will be called.
get and set are what you actually get when you ask for the variable itself. If I set shadowOpacity, it will pass it on to the set code. If I get shadowOpacity, it will actually get me layer.shadowOpacity.
#IBInspectable var buttonBorderWidth: CGFloat = 1.0
In that example, buttonBorderWidth is an actual property of the view. The attributes inspector can write to it and read it directly. The didSet observer is just so that something happens in response to our changing that property.
That's totally different from the other example:
#IBInspectable
var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
In that example, the goal is to make the layer's shadowOpacity inspectable. But you can't do that, because it's not a property of the view. Therefore we put a façade in front of the layer property, in the form of a computed "property" of the view; the attributes inspector can't see layer.shadowOpacity, but it can see the view's shadowOpacity which, unbeknownst to it, is just a way of accessing the layer's shadowOpacity.

How to initialize a variable using another variable in an SKScene

I want to be able to make two variables available to the entire SKScene, and all functions inside of it. One of these variable using the other one to create its value. I understand why I cannot do this, but I don't know a fix for it. I have this code:
class GameScene: SKScene {
let num : CGFloat = 1.25
let reciprocal = 1 / num // <— This Line
override func sceneDidLoad() {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
But I am obviously getting an error the line 4.
Cannot use instance member 'num' within property initializer; property
initializers run before 'self' is available
This means that I cannot use the variable because it is connected to the skscene, and the scene hasn't been implemented fully yet. Is there a way to declare this variable without throwing an error and making it assessable everywhere within this class?
Since reciprocal depends directly upon num, it could make sense to let the prior be a computed property based on the latter
class GameScene: SKScene {
let num: CGFloat = 1.5
var reciprocal: CGFloat { return 1/self.num }
// ...
}
Since num is an immutable property and will never change at runtime, another alternative is to let reciprocal be a lazy variable, computed upon its first use
class GameScene: SKScene {
let num: CGFloat = 1.5
lazy var reciprocal: CGFloat = { return 1/self.num }()
// ...
}
(Or, implement your own custom initializer for the GameScene, where you can initialize num and reciprocal to e.g. a given value and its reciprocal, respectively).

How to control one IB custom view with mouseEntered/-Exited in another IB custom view

I'm sorry for my incompetence, but I'm new to Cocoa, Swift, and object-oriented programming, in general. My primary sources have been Cocoa Programming for OS X (5th ed.), and Apple's jargon- and Objective-C-riddled Developer pages. But I'm here because I haven't seen (or didn't realize that I saw) anything that speaks to this problem.
I want to change the contents of one IB-created custom view, LeftView, by mouseEntered/-Exited actions in another IB-created custom view, RightView. Both are in the same window. I created a toy program to try to figure things out, but to no avail.
Here's the class definition for RightView (which is supposed to change LeftView):
import Cocoa
class RightView: NSView {
override func drawRect(dirtyRect: NSRect) {
// Nothing here, for now.
}
override func viewDidMoveToWindow() {
window?.acceptsMouseMovedEvents = true
let options: NSTrackingAreaOptions =
[.MouseEnteredAndExited, .ActiveAlways, .InVisibleRect]
let trackingArea = NSTrackingArea(rect: NSRect(),
options: options,
owner: self,
userInfo: nil)
addTrackingArea(trackingArea)
}
override func mouseEntered(theEvent: NSEvent) {
Swift.print("Mouse entered!")
LeftView().showStuff(true)
}
override func mouseExited(theEvent: NSEvent) {
Swift.print("Mouse exited!")
LeftView().showStuff(false)
}
}
And here's the class definition for LeftView (which is supposed to be changed by RightView):
import Cocoa
class LeftView: NSView {
var value: Bool = false {
didSet {
needsDisplay = true
Swift.print("didSet happened and needsDisplay was \(needsDisplay)")
}
}
override func mouseUp(theEvent: NSEvent) {
showStuff(true)
}
override func drawRect(dirtyRect: NSRect) {
let backgroundColor = NSColor.blackColor()
backgroundColor.set()
NSBezierPath.fillRect(bounds)
Swift.print("drawRect was called when needsDisplay was \(needsDisplay)")
switch value {
case true: NSColor.greenColor().set()
case false: NSColor.redColor().set()
}
NSBezierPath.fillRect(NSRect(x: 40, y: 40,
width: bounds.width - 80, height: bounds.height - 80))
}
func showStuff(showing: Bool) {
Swift.print("Trying to show? \(showing)")
value = showing
}
}
I'm sure I'm missing something "completely obvious," but I'm a little dense. If you could tell me how to fix the code/xib files, I would very much appreciate it. If you could explain things like when talking to a child, I would be even more appreciative. When I take over the world (I'm not incompetent at everything), I will remember your kindness.
I came up with a workaround that is much simpler than my prior strategy. Instead of having mouseEntered/-Exited actions in one custom view try to control what is displayed in another custom view, I simply put the mouseEntered/-Exited code into the view that I wanted to control, and then I altered the location of rect: in NSTrackingArea.
Before moving code over by for that approach, I had tried changing the owner: in NSTrackingArea to LeftView() and just moving over the mouseEntered/-Exited code. That generated lots of scary error messages (naive newbie talking here), so I gave up on it. It would be nice to know, though, how one can correctly assign an owner other than self.
In any case, any further thoughts or insights would be appreciated.