I’ve tried a couple of things to do the challenge of enforcing a window’s aspect ration through the windowWillResize(:toSize:) delegate method and I wonder if it is actually possible to do it that way.
My simple implementation checks if the new width or height have changed and then I return an NSSize back with modified values. But when I resize the window it correctly keeps its aspect ratio but it also slowly moves on the screen.
The most simple solution here did not actually involve the delegate: I simply set the NSWindow.contentAspectRatio property. But that may be considered cheating :-/
If I drag from the corner and the left and right sides, I see normal behaviour. However I cannot drag from the bottom.
EDIT: I was thinking about what you said about expecting the window not to move. For me, I would expect the element of the window opposite the thing I drag to remain unmoved. So for example if I drag the bottom right corner I would expect the top left corner to remain where it is. If I drag the left edge I would expect the right edge to remain where it is. The constraints we are holding the window to would necessarily change the other dimensions. Let’s not forget that constraining a window’s aspect ratio is a very unusual thing to do and so there isn’t some well defined, correct way for it to behave.
If you expect the top left corner to stay where it is during dragging then dragging the left edge would give the user a very odd experience.
Both of you are on the right track to implementing the solution we were looking for as an exercise in delegation, implementing windowWillResize(_:toSize:). Well done using the window’s contentAspectRatio as well!
To get dragging from both the left/right and top/bottom sides to work, you’ll need to sometimes calculate the height based on the width (NSSize(width: frameSize.width, height: frameSize.width * 2.0)) and sometimes calculate the width based on the height (NSSize(width: frameSize.height / 2.0, height: frameSize.height)) depending on whether the height or width is changing.
It’s true that the window’s resizing may seem a bit wonky at first. Here’s how to think about it: When you resize the window by dragging from one of the window’s edges, notice that the edge follows your mouse, and the opposite edge stays fixed. Similarly, when you drag from the corner, the corner tries to follow your mouse, and the opposite corner stays fixed. This is perfectly intuitive when the we’re not adjusting the window’s size as the user resizes the window. When we are adjusting the size, it does appear a bit wonkier. The reason, though, is that the control point (whether a corner or an edge) that we’re dragging is trying to follow the mouse, even if we’re hampering it a bit. Notice that you get the same behavior when using the contentAspectRatio approach.
For an extra challenge, when live resizing begins, determine whether the user is changing the width or the height, and update the other component (height and width, respectively) based on the first component. To do this, you’ll need to implement windowDidEndLiveResize(_. It will be helpful to have an enum which keeps track of whether the user is changing the height or the width like this:
enum ResizeStyle {
case Height
case Width
case None
}
Simple solution. Works both when adjusting the width and when adjusting the height. Also works from corner point, but a bit more jerky.
Why does this work, but using if-else does not work? (only smooth for one of the sides) I didn’t see any use for windowDidEndLiveResize.
Mackzyme, I’m seeing the same problem with your solution as the OP had. No matter which edge you drag or if you drag from the corner, the top left corner of the window moves in response.
Yes, you’re correct the origin (origo)changes. It’s probably possible to lock that although I haven’t tried. I just posted a solution for the resizing.
I’m confused as to why windowDidEndLiveResize( is necessary to determine whether the user is changing the height or the width. I implemented it here and tested it with a console message, but did not end up using it for anything important. I instead made the height/width determination by setting oldHeight and oldWidth variables to match the initial size values I gave my window in the Interface Builder, and comparing the values coming from windowWillResize( to them. At the end of each call to windowWillResize(_:), oldHeight and oldWidth are updated to the new height and width values being passed back to the window. My solution seems to work fine, but I want to make sure I’m not missing the point of the challenge. Any other comments or critiques are welcome. I’m VERY new at this. Thanks!
var oldHeight: CGFloat = 400.0
var oldWidth: CGFloat = 200.0
func windowDidEndLiveResize(notification: NSNotification) {
println("Done resizing")
}
func windowWillResize(sender: NSWindow, toSize frameSize: NSSize) -> NSSize {
let userSize = frameSize
// println("The user wants a height of \(userSize.height) and a width of \(userSize.width).")
var desWidth = userSize.width
var desHeight = userSize.height
var newHeight: CGFloat = 0.0
var newWidth: CGFloat = 0.0
if desHeight != oldHeight {
newHeight = desHeight
newWidth = desHeight / 2.0
println("User is changing the height")
} else if desWidth != oldWidth {
newWidth = desWidth
newHeight = desWidth * 2.0
println("User is changing the width")
}
let mySize = NSSize(width: newWidth, height: newHeight)
oldHeight = mySize.height
oldWidth = mySize.width
return mySize
}
But that is just natural behavior if you drag it from the lower left or lower right corner. Drag my implementation from the upper right/left corner and you will notice no wandering.
Mackyzme’s implementation has the problem that it has crazy flackering, which makes me crazy.
My question is: Why has the mouse such a strange behavior as soon you implement this delegate method? The double arrow for the resizing cursor always becomes a normal mouse pointer at some points where you should see a double arrow… That’s pretty nasty.
I think your solution is very similar to mine. If you drag on a corner it can cause odd behavior. As it can jump between the two branches of the if statement. So the window flickers. That is, while you are draging from a corner, sometimes you are changing the height, and sometimes you are changing the width. and sometimes you are changing both.
The resizing was very weird for me too. Not to mention keeping the controls looking right (which requires constraints). I fully-formed another idea while looking at this thread. If you have one side twice as long as its perpendicular other side, then their hypotenuse is sqrt(5) times the length of the shorter side. So I took the hypot(width, height) and scaled both dimensions to hypot/sqrt(5) and 2*hypot/sqrt(5) and the resizing was smoother!
I was able to overcome the wandering window problem. When the returned size contains a fractional component for one of the dimensions (i.e. width = frameSize.height / 2.0), Cocoa seems to move the window a bit. I was able to prevent this by applying ceil() to the calculation: