Swift iOS14 DatePicker text alignment - swift

I want my date picker to show the text right aligned as it is contained in an right aligned tableView. I'm using the iOS 14 default style were selecting the date picker ends up in a popover to select the date, so by default the label is left aligned when the popover is not showing.
I tried using datePicker.contentHorizontalAlignment = .right but nothing is changing.
Is there any other way to change the alignment as it is looking strange that way?

There's no explicit way to do this, since the widget itself seems to not honor the content alignment; BUT: I have found a way to do this that works with iOS currently. At some point the date picker creates a UILabel, seemingly with a 'leading' constraint, no matter what content alignment. But, we can override what that 'leading' means with clever use of semanticContentAttribute.
datePicker.semanticContentAttribute = .forceRightToLeft
datePicker.subviews.first?.semanticContentAttribute = .forceRightToLeft
This code can't crash the app, even if the widget changes, it safely access the first subview and sets an official API on it.
It does work with iOS 14.2; and
Supposing, in iOS 14.3 or 15+, Apple changes the widget structure such that this trick doesn't work, perhaps because there's another nested view, all that could happen is the layout goes back to the default.
It's possible only the second line of code is necessary, but my first attempt was with the first line, which didn't work, so I added the second line.

Related

How to avoid NSTrackingSeparatorToolbarItem from overflowing when resizing a SplitView?

I am working on a macOS app and I programmatically adding NSToolbarItems following Apple's documentation and I am adding the new NSTrackingSeparatorToolbarItem to support the new macOS 11 toolbar look and feel.
The tracking separator seems to work fine as long as I don't resize the split view below a certain width, then the separator breaks out and just looks out of place.
The tracking separator working as expected
And the separator breaking out when the split view size is below a certain width.
Is there a way to avoid this?? I know that I can set a minimum size for the split view, but is there a way to keep them synchronized (I don't want to hard code a minimum width)?? Especially if the user adds toolbar items dynamically.
Thanks in advance.
Ok I think I figured it out...
I noticed a interesting behavior when playing around with the Mail app, which when the preview pane is showing the NSTrackingSeparatorToolbarItem follow the resizing of the split views but the moment the preview is collapsed the toolbar changes appearance and becomes like a static toolbar. Furthermore the NSTrackingSeparatorToolbarItem stays in place and looks just like a normal toolbar separator (like you might find on other OS's).
So I played around in IB with my toolbar and discovered my error, my NSWindow had the titlebar set to transparent!!
I unchecked the option and voila I could replicate the look and feel of the Mail app, all automatically.
this property has to be set to false in NSWindow
#available(macOS 10.10, *)
open var titlebarAppearsTransparent: Bool
And of course the toolbar style should be set to unified
#available(macOS 11.0, *)
public enum ToolbarStyle : Int {
// The default value. The style will be determined by the window's given configuration
case automatic = 0
// The toolbar will appear below the window title
case expanded = 1
// The toolbar will appear below the window title and the items in the toolbar will attempt to have equal widths when possible
case preference = 2
// The window title will appear inline with the toolbar when visible
case unified = 3
// Same as NSWindowToolbarStyleUnified, but with reduced margins in the toolbar allowing more focus to be on the contents of the window
case unifiedCompact = 4
}
Please note that the break out behavior still occurs but when this happens the toolbar changes look and feel and you don't see any ugly split view divider line like I had experienced. Hope this helps anyone else who might run into this issue.

Keep cursor position on bottom line in textView Swift

I need to create a UI where, as the user types, the cursor/typing area stays in the middle of the page and the newly typed text is pushed 'up and off' the top of the page.
I've tried to set the cursor position to the bottom of a text view which covers 0.6 of the superview but that doesn't work (presumably because it doesn't have any text?). This is a great answer for general cursor placement but my use case isn't catered for.
The code I used to try place the cursor at the end of the text view is :
func setCursorPosition() {
let newPosition = textViewOutlet.endOfDocument
textViewOutlet.selectedTextRange = textViewOutlet.textRange(from: newPosition, to: newPosition)
}
This doesn't work because it is looking for filled in text but sometimes the text view would be empty and I'd still need to load it on the bottom line.
The effect I'm trying to create would look like this :
quite a complex problem I think - any ideas?
For people who have this problem in future. I solved this by coming at it from a different angle. Rather than attempting to place the cursor half way down the page, I removed the height constraints and made the text view dynamically resize based on the content.
Then I adjusted the constraints (helped by this answer) to fix the space to the bottom safe area but didn't specify a height.
This way, the text view dynamically resizes but in an upwards direction. When it reaches the gradient I have placed over the frame it starts to fade out.
Perfect (I also disabled scrolling to make this work)
Top tip - if something seems really hard there might be a different way to approach the problem!

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!

UIStackView and Multiline Labels?

I've tried to get this to work but interface builder is doing my head in and I was wondering if anyone has a proper solution for this.
So I want to have a stackview that contains a label with multiple lines inside. The first hit on google (read here) tells you to embed the label within a view (A), then drag that view into another view (B) (because the embedded view (A) has a 20pixel pad) and then unembed the first view (A).... voila. Except this only tricks the stackview for horizontal stacks and if you continue to stack the view you will still have a ton of problems (I will demonstrate with screenshots later in the question).
I found another guide on google that criticizes a potential fix for the problem, fixing the stackviews width. The author makes the point that Apple didn't intend for you to be doing that, after all it's supposed to be auto layout, not fixed layout. This guide theorises that the issue is just a bug and that you can create a stackview with a single line label, set it all up and THEN add the multiple lines. I tried this and it didn't work, it simply messed the entire stack view up! The stack view will warp and essentially break unless the label has number of lines set to 1 and only 1. (Image 1) Stackview with label set to have multiple lines.
So I talked earlier about embedding the paragraph label within a view. This doesn't cause any errors in the storyboard,(Image 2) you can't really align the text properly with the other UIStacks but that's not an immediate problem for me to solve. What IS a problem is what happens at runtime....
Simulator, Portrait (Image 3), Simulator, Landscape (Image 4).
I tried running this on an actual device to see if it was just a simulator bug but the same thing happened on my iphone 6. Safe to say this is probably not going to work!
Oh and just before we go any further, I am building the stack views in the following way:
[name - placeholder]
[phone - placeholder]
[address - placeholder]
, setting a spacing of 8 and equal fill
,vertically stacking all of these stacks (3 into 1) and setting a spacing of 8
, vertically stacking the details titles with the big stack and 8 spacing
,finally adding the title and button into the stack view with a spacing of 32.
I then apply some storyboard constraints: in this case just centre vertically & horizontally, so the view is always centred and displays properly in both screen orientations.
Even though this isn't aligned properly, this is the view before applying the stacks and constraints, this is what I want my endgame to look like: Looks kind of silly, but I want to figure this out so I can actually stack my paragraphs! (Image - 5).
So now hopefully I've established that you can't really embed the label within a view as it doesn't render correctly at runtime, and you can't trick the stack into adding new lines.
How do I put a label with multiple lines into a stack view safely?
This is almost an offshoot question but when you try applying a stack to a paragraphed label, it sets the width of the label to be absolutely massive (sometimes it throws an error that interface builder can't render it Demonstrated here (Image 6). I've seen this happen a few times and don't really understand how xcode thinks that is a sensible option.
I don't want to apply fixed widths to my labels or stacks because I'll only be left with another warning, and warnings are bad!
I'm pretty lost at what to do, if anyone knows of a way this can be achieved I would be eternally grateful!
Thanks
This really seems to be a bug.
A workaround that works for me is embedding the multiline label in a view and leaving it there.
That fixes the layout on the Storyboard editor and also works in the simulator.
A weird thing is that if I have several multiline labels on the same StackView I only have to embed one of them in a UIView, and then all the other multiline labels will behave properly.
While I do agree this is an issue in UIStackView as layouting should work according to the intrinsic size of the UILabel.
When using stackview with multiline label, it is not able to update its size according to the label.
For resolving this issue, use preferredMaxLayoutWidth property of the label and set it to any value.
label.preferredMaxLayoutWidth = 1
Note: This even worked for me when I set it to 1
The preferredMaxLayoutWidth property as defined in the Apple Docs:
This property affects the size of the label when layout constraints are applied to it. During layout, if the text extends beyond the width specified by this property, the additional text flows to one or more new lines, increasing the height of the label.
which we do want to increase for a vertical UIStackView.
the solution is about embedding the UILabel inside "view without inset".
and then update your constraints.

iPhone: varying transparency of previous/next button on inputAccessoryView

Short version: I have a working solution using a UISegmentedControl but I don't like how it looks. When I use Safari, it "dims" either previous or next when it's at the beginning or end of the list of input fields. I can't convince a UISegmentedControl to do that, so how did they do it?
Long version:
I have a UITableView with cells that have text fields. The text fields take numeric inputs (floating point numbers, generally). That means I present the keyboard with keyboardType = UIKeyboardTypeDecimalPad. That also means no return button, so I need a good method to move between cells OTHER THAN touching the cells. I'd like to do it the same way as, say, iOS Safari does it - with a little toolbar that says "previous | next ... done."
I have code that does all of that (sets the textField.inputAccessoryView to be an instance of a UIToolbar with the appropriate buttons), using a UISegmentedControl for the previous and next button. I have that working so that next moves to the next cell and previous moves to the previous cell.
So why am I here? I can't get the dimmed previous/next behavior seen in iOS Safari to work with a UISegmentedController and I don't really want to implement all of that myself if I don't have to (at that point, my CCB will invoke one of the two rules it uses to reject changes: Rule 1) If I don't think it's a good idea, then I won't implement it; Rule 2) If it's easy, I'll probably do it).
(I would have posted pictures, but I'm too new to SO - sorry)
When I try to do the same thing it looks and acts like a Safari middle text field, but I can't seem to get it to look like the Safari version of first or last text field.
I've tried enabling, selecting, even changing the bar background color, but none of them seemed to work. The background color seemed to get closest, but I had to have 3 different toolbars based on when it was first, last, or middle, and even then it wasn't great.
Solved. The issue was that the "enabled" setting of the various segments as set in IB does not make it to the executable. I have to programmatically disable the previous or next segment in the view controller. Once I did this, it now appears as I want it.
When I set up the UISegmentedControl in IB, I set one segment to enabled and the other to disabled (== not enabled). In the debugger I started trolling through the settings of the various segments and found that actually setting the segment to disabled makes it work in the way I wanted.
For example, in the "nextToolbar" that contains a UISegmentedControl that is attached to "nextControl," only the Next button should be shown as active. That means I have to set the Previous button to disabled. The Previous button is at index 0, so disabling it means to do this somewhere before it's used (I put this in viewDidLoad:):
[self.nextControl setEnabled:NO forSegmentAtIndex:0]
Similarly, for the prevControl, where only the Previous button should be active, I use:
[self.previousControl setEnabled:NO forSegmentAtIndex:1]