Available height to position UI elements assuming keyboard always shows - swift

I am encountering a problem that I'm not sure how to solve. If you look at the screenshot below, you'll see that the UI is laid out so that it neatly fits into the space between the safe area layout guide's top anchor and the top of the keyboard.
The keyboard will ALWAYS be shown in this view controller. But I need a way of knowing what the height of the keyboard is so that I can do some math to figure out how to tall to make the UITextView (the red box).
I tried the approach of registering for notifications on when the keyboard will show, but that is too late.
I need a way of knowing the height of the keyboard before the view renders so that I can configure the height of each of the elements in the UI.
Is there a way to do this?
Thank you!

One option would be to capture the dimensions of the Keyboard when it IS presented then pass those dimensions into this view.
However, you will also want to make sure that you are handling cases such as the split keyboard on an iPad and things of that nature.
For that you might choose to look at the documentation on using the keyboardLayoutGuide and use it with some constraints.
https://developer.apple.com/documentation/uikit/keyboards_and_input/adjusting_your_layout_with_keyboard_layout_guide

Related

How to go about implementing a custom TextView in GTK3?

I would like to write a very simple text editor with a custom widget for drawing the text. I use GTK3. So far, I've come up with something like this:
Window -> Vertical Box -> Scrolled Window -> Drawing Area
The scrolled window fits the main window, as it is inside a vertical box. Depending on the size of the drawing area, the scrolled window shows the scroll bar. So far so good.
Now, there are several things I don't understand:
When the text doesn't fit the drawing area, how do I go about resizing the drawing area?
What is the best way of approaching the resizing when the text changes? One way I can think of is to simply handle key presses and count lines, but that doesn't seem right and probably won't work when lines wrap.
What events do I have to handle (and possibly propagate) for this to work?
I've tried searching the GTK docs, but they are very unhelpful in this regard. gtk3-demo doesn't really help, too. I know that there is https://developer.gnome.org/gtk3/stable/ch01s05.html, which describes the custom drawing a bit, but doesn't solve what I'm trying to do.
Update:
I figured out a lot by reading gedit, gtksourceview and gtk sources, which I wanted to avoid, but there seems to be no way around it.

NSOutlineView disclosure triangle vertically off center

I'm working with an NSOutlineView on a macOS app and it provides disclosure triangles for items that can be expanded.
I'm also creating custom NSTableCellView items rather than using basic cell items. This allows me to create the cell how I want visually.
My issue is that when they're displayed, the disclosure triangle on the left is not centered vertically.
Notice how the disclosure triangles are not aligned properly. They're a bit lower than they should be. If you scroll away and come back, sometimes, they automatically align themselves correctly. Has anyone been able to fix this issue before?
For what it's worth, I'm using the following code as well for the cells.
self.outlineView.rowHeight = CGFloat(integerLiteral: 66)
self.outlineView.usesAutomaticRowHeights = true
It's hard to figure out what the problem is without seeing how you've set up your project, but I'm going to give it shot.
First, when usesAutomaticRowHeights is set to true, the outline view uses Auto Layout to position the cell views. Thus, you need to be utilizing constraints in your Storyboard or Nib file, or things will behave strangely (see: your picture). If I had to guess, the prototype cell view you set up in Interface Builder is having its autoresizingMask translated into Auto Layout constraints (which, generally, causes a boatload of problems).
What I would do is this:
Open up the Storyboard or Nib document containing the outline view.
Locate the prototype NSTableCellView instance that contains the street name text field in the Document Outline to the left of the canvas. (If you don't see the Document Outline, you can open it by clicking the item at Editor » Show Document Outline in the main menu).
Next, see if you have any constraints in place. If you do, remove them by selecting Editor » Resolve Auto Layout Issues » Clear Constraints under the menu item "section" that's titled All Views in ${YOUR_SCENE}.
Now, depending on what you're going for, there are different ways to go about setting up constraints, but here's what I would suggest. Assuming you want the street name to be centered vertically with the disclosure triangle, I would add a vertical constraint between the text field and its parent cell view like so:
With the text field selected in the Document Outline, click the Align icon in the lower right-hand corner of Interface Builder's main canvas area (see image).
In the popover that appears, check the checkbox next to Vertically in Container.
In the text field on the right side of the popover, enter a value of “0”.
Finally, click the “Add 1 Constraint” button.
You’ll probably see a red error (or yellow warning) sign show up, as the view has now opted into Auto Layout, but it only has a metric for its vertical position. So we now need to add some constraints to describe where the text field should be positioned on the x axis. Like before, we’ll define the constraints using the popover buttons on the lower right-hand side of the canvas:
Click the Add New Constraints button (the one to the right of the Align button).
On the diagram at the top of the popover, click the faint red lines on the left and right side of the white rectangle. This is telling Interface Builder we want to add leading and trailing constraints.
Now, enter the desired padding you want on each side of the text field. In the example image, I went with “4” points on both sides, but obviously, you can use whatever value(s) you think works best with your layout.
Finally, click the “Add 2 Constraints” button.
Any warning(s) that were present should now disappear, as we've added enough constraints to describe the position of the text field. In theory, you should now be able to build and run your project, and the text fields should be aligned with the disclosure triangles. With that said, there are plenty of other reasons a layout can get finicky, and considering usesAutomaticRowHeights is a new API in macOS High Sierra (and Steve Jobs is no longer there to beat it into Apple developers to make everything Just Work™), there could be issues that I'm unaware of.
Alternatively, you can set usesAutomaticRowHeights to false and have some object (e.g. a view controller, a NSObject subclass, etc.) conform to the NSOutlineViewDelegate protocol and implement the outlineView(_:heightOfRowByItem:) method to return any arbitrary height you want for different rows. The nice thing about sizing rows this way is that you can allow certain rows to be larger or smaller, depending on the role of the corresponding item. There are lots of tutorials on this, so I won't regurgitate a half-baked explanation here, but feel free to Google “Conforming to NSOutlineViewDelegate protocol” for more info.
Anyway, try the steps above, and see if they do anything for you, and if they don't, let me know. I can go as deep into the rabbit hole with you as you need, so just ask. Good luck!
For those like me who stumbled upon this issue many years later, here's the fix that worked for me, and requires a lot less work.
NSOutlineView has a function frameOfOutlineCell(atRow:) and the documentation states: You can override this method in a subclass to return a custom frame for the outline button cell
You can override the method in order to provide a frame that's actually in the vertical center of the row. And an important point that I learned from trial and error, is that you don't even need to provide that updated frame. Not sure if this is a bug or what, but for me, just calling super.frameOfOutlineCell(atRow: row) in the function override was enough to make the disclosure indicator appear in the correct location.
So my subclass of NSOutlineView is this:
class MyOutlineView: NSOutlineView {
override func frameOfOutlineCell(atRow row: Int) -> NSRect {
super.frameOfOutlineCell(atRow: row)
}
}
That's all. Hopefully that works for others as well!

Multiple scrollbars when there is a pop-up or modal window. What is the reason for implementing this design pattern?

I have noticed on certain websites that are implemented as a single page and free scrolling view in browsers that when you trigger a pop-up or modal window a second scrollbar appears to allow the user to scroll through the content of the window. The primary scrollbar is also active but not very effective since the pop-up or modal window blocks out most of the screen.
I cannot imagine that this implementation approach would have very strong usability reasons, so I was wondering if there is a valid technical reason or constraint. But if there is a valid argument from a UX design perspective then I am also interested in finding out the rationale behind it.
When content overflows (too big to fit in the container element), the browser adds a scrollbar. Looks like in your case, the modal is taking up 100% width/height of the viewport, but it's still too small to fit all the content, hence the vertical scrollbar.
To get rid of the scrollbar, people usually add overflow: hidden on the element which hides the scrollbars, but also doesn't allow the user to scroll. This is fine if the content is not important... such as a graphic that's meant to be a background image or something of that sort.
It's poor design in my opinion. If there is enough content that the user will have to scroll to see it, there's no reason to put it in a modal. If you have a modal, keep it small. In some cases where it's necessary though, you can hide the main scroll-bar when the modal is in view.

XCode 4 Interface Builder: A better way to work with lots of overlapping views

In IB I have quite a few views that are shown. Many of them are hidden when the app loads, but are shown later when buttons are pressed. This is all fine, but when building this layout in IB it is extremely difficult to layout anything because there are so many overlapping views, some of which are partially transparent (ones that are set to hidden) and other are completely overlapping and covering others. This makes layout very hard.
What is the best method when laying out lots of views like this? Is there another way to break things up? Or better yet, can I hide a a view completely (like in photoshop) so that I can edit the ones underneath, then turn that layer back on?
Another option when trying to select a view that is obscured by another is the shortcut:
'ctrl' + 'shift' and click
It displays a list of all the views under the cursor.
I'm not aware of any way to hide objects in the canvas, but a useful trick for complex layouts is to double-click an item in the document tree to the left - this selects the item and puts focus on the canvas, you can the use the cursor keys to nudge it about.
This doesnt solve the problem of not being able to see things because there are, for example, five or six labels occupying the same space, but if that is the situation it may be a better idea to have a single label and change its contents in code.
I ran into this issue for an app I'm building that has an arial-view image of a park with clickable hotspots. When a hotspot is clicked a popup UIview is displayed with information about that spot in the park. I use the same VC/XIB for three parks. This makes the XIB really busy and hard to work with (i.e the same issue that you have) The detail UIViews make it hard to work with the views underneath. My workaround was to pick each detailed UIView that was hiding the part of the XIB I wanted to work on, and add 1000 to the UIView origin.x in the size inspector. This moved those UIViews enough out of the way for me to do what I needed to with the XIB. Then when I was done, I moved them back by x 1000. (I just needed to move them out horizontally to do what I needed to)
I know its clunky but given that XCode does not have a convenient way to hide portions of an XIB - it was the quickest approach I could think of!
One approach to handling overlapping items in IB is:
Ensure the groups of items that you want to hide are grouped into Views.
Give these Views names: e.g. ViewOptionA, ViewOptionB and ViewOptionC.
Can do this by clicking on name of view in the tree while it is selected and then typing new name.
When you want to hide one of those groups of items:
a) Select the View by either:
i) Clicking on it in the tree at the left or
ii) Ctrl-Shift Clicking in the layout editor and then select the view from the list.
b) In the Attributes Inspector set Alpha to 0.
When you want to unhide one of those groups of items:
As for 2) but set Alpha back to 1
[You do need to remember to unhide all views before you publish!
If you are forgetful like me then perhaps you could subclass UIView and set Alpha to 1. I haven't tried this subclassing idea yet.]

Vertically add elements to a scrollview: diffs between Java and iPhone SDK?

Folks,
coming from the Java/Swing world, I am sometimes puzzled by the UI programming on the iPhone. I've spent the last hours with Googling for information but it seems that I asked the wrong questions by thinking too much in Java. Could you please point me to resources to get more familiar with the GUI concepts to be able to achive the following functionality:
Basically, I want to create a vertically scrollable view that represents structured text (for example, a recipe). Each step consists of a title and a textual description. As I want to fold and unfold such steps, the title would be somehow interactive and by clicking it the description would be displayed or hidden.
In Java, I would create a component that renders one such section. The layout manager would compute the components preferred height (with or without description being displayed).
Then, in Java, I would create a panel with a vertical layout manager and sequentially add my components. This panel would be placed in a scroll pane; the scroll pane would ask the panel to layout itself and then show a viewport on it, if for example the height is bigger than the scroll pane's height.
What Java provides me is:
layouting of elements (computing their preferred height and width), thus no need to deal with coordinates and dimensions
dynamic creation of UIs by creating and adding components during runtime
What I understood on the iPhone:
I can dynamically add views as subview to a view, e.g. a scrollview by calling addSubview
I can even remove that stuff using removeFromSubview (as explained here Clear content of UIScrollView)
What I don't understand on the iPhone:
does one view always correspond to a visible screen (I did use tab and navbar navigation so far and there whenever I set a new view, it fills the current visible screen minus the space needed for the two bars)?
or is it possible to define a view that contains a label on top ("north") and a text in center; if so, could such a view automatically determine its height?
can I realize my example in a similar way like in Java or would I need to calculate all dimensions and coordinates of the individual components on my own? (This example seems to touch on that topic: iPhone scrollView add elements dynamically with id)
Alternatively, could I use a WebView and render my stuff as local HTML using JavaScript to show or hide nodes?
Thanks for any hint or link!
There are no layout managers in Cocoa, views are being reposition according to their struts and springs settings. For information on that read the documentation: http://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/IB_UserGuide/Layout/Layout.html
To create a "view that contains a label on top and a text in center" you create a view with subviews - one being a label at the top, second the textview in center. If you configure struts/springs for all of subviews properly, they will autoresize when the container view is resized.
You should also get accustomed to Interface Builder, creating views in code is real pain in the ass.