Buttons become mis-aligned when window is resized - cocoa

I'm designing my interface in Interface Builder (using Xcode 4.2 on Snow Leopard), and PERFECTLY aligning two elements (two NSButtons, bordered), one below the other.
The thing is that when the window is resizing, at some points, the elements seem misaligned (by 1 pixel or so), while at some other, they're still perfectly aligned.
Here's a (zoomed) example of what I mean:
Aligned
Mis-Aligned
And here are my resizing settings (for the upper NSButton):
And for the container (of my upper NSButton):
I know I'm probably getting a bit too crazy about such a tiny issue, but I definitely need to resolve it.
So, why is that happening? What should I do in order to resolve it?

Are both buttons in the same container?
Do they have the same size & alignment settings?
Below the autosizing widgets in the inspector, there's a set of alignment buttons. Try selecting both buttons and clicking the left-side alignment button. (See if that makes the other side mis-align.) Below that are placement icons - verify both buttons have same settings there.
Type in values for W & H so both buttons are the exact same (even if the boxes already show the same, type over it to be sure). Also type in X & Y so they're same (except for vertical offset).
Personally, having the center scaling set (last image, double-ended horizontal arrow) seems odd when it's only anchored on one side. That might have a strange effect. On the other hand, you have both vertical anchors set but not the vertical scaling.
If all else fails, you could try (save original version first) delete the second button, copy the first and position the copy below it.

Related

Placing window on two displays with different resolution in same point

Is there a way to place window in the same point for example in top-right corner on the to displays with different resolution?
For example you have Macbook and you connected it to big display.
Note: windows property "Spaces" in IB is set to "Can join all spaces"
Spaces and displays are two separate concepts. So, "Can join all spaces" is not relevant to your question.
A window can only be at one position in the global screen coordinate system that spans the whole desktop. Each display constitutes a separate part of that coordinate system (ignoring mirroring). Therefore, no, it's not possible to have a window show up in the top-right corner of two separate displays. You would need two separate windows to achieve that.

Win81+ GetWindowRect Not Including Complete Drag Area?

I am trying to get the width and height of all the windows. I did this easily with GetWindowRect however the styling in Win8.1+ seems such that there is a border on some windows and thats not being included. When I move/drag the window, this area moves with the window, so I expected it would be apart of its geometry. Is this known? Is there away to include the border width?
New Example
I created a second screenshot to explain after I see some confusion in the comments.
I have two windows side by side as seen in this image here:
Now if I take the GetWindowRect of the left and right windows, it should be a continuous rectangle around both of thse windows. However in the below we see this is not the case. I put a black fade over the whole desktop and cut out just the parts of the GetWindowRect for each window, we see the left window GetWindowRect is a bit smaller, this is my problem.
Old Example
For example this is a screenshot, using the cleared/non-black curtain area is what GetWindowRect identified as the width height, x and y:
We see there is some area of the window not included, I think this is the border? I used photoshop here to put a blue border around what all should have been:
And just for clarity I put an inner red border to show that the area between the red and blue borders was what should have been included, but was not:
Does anyone know how to include this "border" in the GetWindowRect?
Yes, GetWindowRect() lies to you. As you found out. It is a necessary lie. Goes back to Vista, the first Windows version that gave resizable windows their fat border. A pretty necessary feature, high screen resolutions were getting pretty common and the traditional 2-pixel border was getting too hard to hit with a mouse.
That created a massive compatibility problem however, many programs that create a window use CreateWindowEx(), you specify the outer window size. And don't use AdjustWindowRectEx(), the function that you must use to calculate the window size you need. Necessary because almost every window actually cares about the client size of the window. The part you fill with content. If they would have done nothing then legacy programs would end up creating windows with a client area that is too small, no longer fitting the content or aligning it out of whack. Very, very ugly.
So GetWindowRect() lies and pretends that the window has the traditional 2-pixel border. Pretty consistent lie, if you ask for the border size with GetSystemMetrics() then you get 2 back, even though it is 5. All works pretty well, until you start caring about positioning windows next to each other. Impossible to lie about that.
Turning off the lies requires letting Windows know that you are aware of the consequences of the fat borders. And know how to deal with Aero being turned off, possible on Vista and Win7. You must select the sub-system version number in the EXE file header and pick at least 6.00. The vast majority of programs use the legacy value, 4.00. Exactly how that's done depends on your tooling, for the Microsoft linker it is the /SUBSYSTEM option. It can be changed after the program was built with Editbin.exe, /SUBSYSTEM option. The program will no longer run on XP and earlier.
Windows 10 took an interesting new approach to this problem. The skinny borders are back. But now with a much bigger drop-shadow, the mouse is active beyond the border, even for windows that don't have a shadow. Works pretty well, hopefully we can all forget about this appcompat detail soon :)

Autolayout - equal distribution of 6 views

I want to have 6 objects (buttons) laid out inside one view. They should, however, follow some constraints:
Two top buttons should have the same vertical distance from superview (A)
Two bottom - the same (C)
Two in the middle should have their centers at the superview's center line
The vertical distances between all buttons (E) should be the same
and last but not least - the buttons should be square (so the width and height should be the same)
A = C
B = D
Is it possible to have this effect just in the IB, or should I use some additional code for the constraints?
This is a logical request, but constraints are defined using the attributes of views, but cannot not be defined in relation to other constraints. That having been said, there are a number of approaches:
Layout guides: An approach which doesn't require predetermining the any spacing is to have UILayoutGuide objects or, if using iOS versions before 9, just use hidden views, i.e. views with clear background or alpha of zero, in between the buttons.
The idea is to add these layout guides with addLayoutGuide (or add invisible views with addSubview if supporting iOS versions predating iOS 9) in between your six buttons as "spacers", and define the spacers to be the same size as each other, and with constraints between the spacers, the superview, and the buttons that will go in between the spacer. Once you lay that out (showing the horizontal spacer views in blue, vertical ones in red, just so you can see them):
The equivalent VFL for the constraints for those red UIView objects, called vspacerX, would be:
H:|[vspacer1][button1(100)][vspacer2(==vspacer1)][button2(==button1)][vspacer3(==vspacer1)]|
H:|[vspacer1][button3(==button1)][vspacer2][button4(==button1)][vspacer3]|
H:|[vspacer1][button5(==button1)][vspacer2][button6(==button1)][vspacer3]|
And constraints on the blue UIView objects, called hspacerX, like:
V:|[hspacer1][button1(100)][hspacer2(==hspacer1)][button3(==button1)][hspacer3(==hspacer1)][button5(==button1)][hspacer4(==hspacer1)]|
V:|[hspacer1][button2(==button1)][hspacer2][button4(==button1)][hspacer3][button6(==button1)][hspacer4]|
You don't have to use VFL to define these constraints, as any way you define these constraints will work, but it's just a concise format for describing the collection of constraints that I employed.
Anyway, when the view is rendered with those layout guides (or invisible views), it yields evenly spaced buttons like so:
Another approach is to have six "container" views, that would look like:
The equivalent VFL for these six container UIView objects might look like:
H:|[container1][container2(==container1)]|
H:|[container3(==container1)][container4(==container1)]|
H:|[container5(==container1)][container6(==container1)]|
V:|[container1][container3(==container1)][container5(==container1)]|
V:|[container2(==container1)][container4(==container1)][container6(==container1)]|
You can then add your buttons to that, centering one on each of the six little containers and then make your containers clear:
This works, too, but just a slightly different spacing (where the margins are half of the spacing between the views, whereas the other approach keeps the margins the same as the spacing between them.
Stack view: In a permutation of the prior point, in iOS 9, you can also use UIStackView, designed precisely for evenly spacing views. In this case, put two buttons each in three horizontal stack views, and then place those stack views within a vertical stack view. This achieves six evenly sized container views.
See WWDC 2015 video What's New in Cocoa Touch.
The problem with stack views is that they can be used to ensure even spacing between the arranged subviews, they don't ensure spacing before the first arranged view nor after the last arranged view. So, the kludge to get around that is to, for horizontal stack view, include two more zero width views (or zero height for vertical stack views). Then when you use even spacing on the stack view, it also give you what will appear to be spacing before and after all of the arranged subviews.
NSLayoutAttributeCenterX with multiple: Another technique involves defining the attribute:NSLayoutAttributeCenterX and attribute:NSLayoutAttributeCenterY attributes for your six buttons, but rather than using the constant values, use the multiplier field. This technique enjoys a little simplicity, but doesn't always render the desired effect, so I won't describe it unless it's something you definitely want to pursue. I've already entered tl:dr territory here.
Collection view: Another approach is to use a UICollectionView, which handles this scenario gracefully. It's well designed to let you layout cells in a grid.
Hardcoding values: For the sake of completeness, I'll note that you could simply specify specific values for A, B, C, and D (as well as the width and height constraints). You don't even have to worry about setting the E constraints, but rather just set the vertical center constraint of the middle two to their superview, and you're effectively done (because the spacing represented by E should be a natural result of the previous steps, assuming A=C and B=D). If you want to adjust these values on the basis of device size and/or orientation, you can then implement a viewWillLayoutSubviews to adjust the constants for these constraints according to the size of the view.
Update: I have a better solution that does not use spacers. Check it out here.
Ok, this can be achieved very quickly in IB. It's so so simple. Here's a diagram that will help illustrate.
Assume v1-6 are your buttons, and s1-5 are your spacers.
1) in IB control drag out all of the connections shown by the red lines.
2) shift click v1-6 and pin icon (looks like |-I-| ) set the width and height to a definite value. also, set the height and width to be equal.
3) shift select s1-4 (not 5) and set the height to equal. do not give it a definite height, since this should be calculated by the system. you might also need to set the widths of s1-4 to be equal, but don't give them a definite width.
4) control drag from the centre views to the leading and trailing edge and set the centre constraint.
So, you might think, ok, this should work now. It doesn't. Here's my app running in portrait with slightly different colors. Looks good. (Notice, you would make the spacers invisible once you get it setup).
But when I rotate, oops!
What's happening here? The problem is incredibly easy to solve once we understand what's gone wrong. What we want is for IB to not shrink our views. We want IB to make the spacers and the spaces to shrink and grow as necessary, but to leave our views alone. Basically, IB has shrunk the spacers down as far as it can in portrait and to attempt to make everything fit IB has shrunk our views. But we wanted IB to shrink the vertical spaces between views and spacers, not our views. The solution is so easy. All we have to do is adjust the priority of the vertical spaces and all is well. So, select the vertical spaces in IB and adjust the priority to 750. The vertical spacing lines will show as dashed. Done.
Ok, so here's everything as we expect it.
And with the spacers made clear:

GetTextExtentPoint32 returning values a little off, glyphs truncated

For all strings, the GDI function GetTextExtentPoint32 seems to be returning a width always a little smaller than what ExtTextOut displays:
Above the right red arrow, "buggy," is displayed as a block with ExtTextOut: no problem.
Above the left red arrow, "buggy" is displayed with ExtTextOut, then ", " is displayed width pixels later, where width = GetTextExtentPoint32("buggy"). width seems a little too small.
Using bigger font sizes and dark backgrounds:
Again, "00" and "()" are displayed in different ExtTextOut calls with GetTextExtentPoint32("00") pixels between them.
Any help appreciated.
I’ve found the same problem dealing with monospaced True Type fonts (Lucida Console) and OPAQUE background mode. The problem seems that you call ExtTextOut with correct x coordinate, but the function paints background starting at x-1, which is something I didn’t expect. Negative shift may be higher on larger font size. Glyphs are finally correctly positioned, but that "overpaint" is a problem unless TRANSPARENT background mode is selected. Previously, I thought that I didn’t need to specify the rc parameter, since I didn’t need clipping at all as all character runs were non overlapping, but eventually I’ve had to provide an apparently redundant RECT and ETO_CLIPPING flag, just to prevent this horizontal negative overpainting.

Scrollbars for Infinite Document?

Is there a standard Aqua way to handle a practically infinite document?
For example, imagine a level editor for a tile-based game. The level has no preset size (though it's technically limited by NSInteger's size); tiles can be placed anywhere on the grid. Is there a standard interface for scrolling through such a document?
I can't simply limit the scrolling to areas that already have tiles, because the user needs to be able to add tiles outside that boundary. Arbitrarily creating a level size, even if it's easily changeable by the user, doesn't seem ideal either.
Has anyone seen an application that deals with this problem?
One option is to essentially dynamically expand the area as the user scrolls through it - any time the user scrolls within X units of an edge, add another unit in that direction. Essentially, you'll never be able to scroll "all the way" to an edge, because the closer you get the farther it will expand.
If the user scrolls back away from the edge, contract it to back to no more than X units beyond where there is actually content.
Have you seen what Microsoft Excel does for this problem? It has to represent an unbounded space with scrollbars, as well.
One solution is to define a reasonable space for the original level size, and when the user scrolls to one tile away from its bounds, add another row or column of tiles, and adjust the scrollbar accordingly. This way, the user never reaches the actual bounds.
If the user decides to cut down on the level size, you could also add code that shrinks the "reasonable space" once an unused row consists only of empty tiles. This saves the user from being stuck with a huge level that they scrolled through, with no way to shrink it.
Edit: Same as Dav's answer. :)

Resources