Why is my UIlabel displaying points of ellipsis (dot dot dot)? - swift

I am trying to print a text string that includes the current time on a UILabel.
var currentDateTime = Date()
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .medium
var savedNumberPlusDate = "\(counterValue) : \(dateFormatter.string(from: currentDateTime))"
saveLabel.text = savedNumberPlusDate
The first part works great, but the time comes in as points of ellipsis or dot dot dot (...)
Can't figure out what I am doing wrong.

It's because of label size - not all content fits into it's bounds, so rest is cut. There is UILabel property - Line Break that defines what to do with extended content. But the solution you are probably looking for is to adjust it's size. You can do it via autolayout constraints.

When I have programmatically set UILabel.text, if the length of that string is greater than the length of the text I set in the storyboard, I've gotten the "..." truncation.
Instead of guessing the correct number of points to set as my label's width, I've set the text in the storyboard to be a template for the values I'll be setting programmatically. In your case, I would set the UILabel's text in the storyboard to be something like "9 : 99:99:99 XM".

Well I found the answer. My UILabel on the Storyboard is too narrow, and so it is cutting off the time, and so adding the ...
The simple fix is to drag the label bigger.
The better fix is to use AutoLayout so this doesn't happen again. For example in this SO answer: [How to created an expandable label in Swift][1].
Fixed it.

Related

Number of characters that fit in NSTextView

Note: This question is for a MacOS app and not for iOS
I have a business requirement to convert this amazing iOS custom text view(ReadMoreTextView) to macOS(AppKit). However I am finding it difficult to convert it. I was hoping it would be easy since both UITextView and NSTextView use NSTextContainer, NSLayoutManager and NSTextStorage. But it seems like they behave differently on both platforms.
I need to calculate the number of characters or character range that is visible on the NSTextView. I am trying to use the following method but it is always returning the complete character range instead of visible character range(actual text length instead of visible text length). I couldn't find any other method in layout manager which can help on this. Please let me know if you have any pointers on this.
Here is the code:
extension NSLayoutManager {
func characterRangeThatFits(textContainer container: NSTextContainer) -> NSRange {
//this is the current maximum number of lines
//container.maximumNumberOfLines = 3
var rangeThatFits = self.glyphRange(for: container)
rangeThatFits = self.characterRange(forGlyphRange: rangeThatFits, actualGlyphRange: nil)
return rangeThatFits
}
}
Answers in Swift and Objective C are welcome.
Update:
Please ignore any scrolling on the textview since I am disabling all types of scrolling. My intention is to have a view with "... More" button at the end of it then on tap of it the view gets expanded. For this reason NSTextField based solution is also welcome if we can find visible characters in that.
You can get the visible portion of the NSTextView using its visibleRect method. From there, you just need to get the text range that falls within that rect. So, if your view supports vertical scrolling only, something like this should work:
let visRect = textView.visibleRect
let layoutManager = textView.layoutManager!
let container = textView.textContainer!
let glyphRange = layoutManager.glyphRange(forBoundingRect: visRect, in: container)
let charRange = layoutManager.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
Be aware that if your NSTextView supports horizontal scrolling rather than just vertical, you'll have to do a little more work than this, since you'll possibly be looking at a non-contiguous block of text. In that case, you'll have to get a bunch of smaller ranges for each visible line fragment instead of just grabbing the whole range like this.

NSTextView in NSOutlineView with IntrinsicContentSize setting wrong height

I have an outlineView in which I am putting NSTextViews that resize when edited (think outliner app). I have most of this working, but some behaviour is inconsistent.
On my NSOutlineView I set:
outlineView?.usesAutomaticRowHeights = true
For my cell-views I subclass NSTextView. I set the following auto layout bits:
self.translatesAutoresizingMaskIntoConstraints = false
setContentHuggingPriority(NSLayoutConstraint.Priority.defaultHigh, for: NSLayoutConstraint.Orientation.vertical)
And I override the intrinsic content size calculation on the NSTextView:
override var intrinsicContentSize: NSSize {
guard let manager = textContainer?.layoutManager else {
return .zero
}
print("\(manager.usedRect(for: textContainer!).size) \(string)")
return manager.usedRect(for: textContainer!).size
}
(I was calling ensureLayout on the layoutManager in the code above but it adds nothing)
intrinsicContentSize is called twice per text view when they are added to the outliner. The first time the size returned is correct, but on the second call some of the text wraps unnecessarily. A printout of the two passes on intrinsicContentSize for 4 text views are shown below. The column width is 281, so none of these strings should wrap. The first pass they all fit to one line (14 high), on the second pass, the last two strings wrap, which is strange because they are not the longest strings:
(178.744140625, 14.0) New pointwddwek kelekwelek...
(100.720703125, 14.0) Related Subjects
(119.400390625, 14.0) Related Publications
(87.150390625, 14.0) Related Terms
(178.744140625, 14.0) New pointwddwek kelekwelek...
(100.720703125, 14.0) Related Subjects
(74.705078125, 28.0) Related Publications
(54.484375, 28.0) Related Terms
It is consistently the same strings that result in the same behaviour. E.g. the string "Related Subjects" never wraps, the string "Related Terms" always wraps.
When the views are presented, the text is NOT actually wrapped, even thought the usedRect value implies that it would be. The text is shown correctly, but the row view in the outliner is too high because it thinks it has two lines of text.
Any pointers where I might be missing something? Does 'ensureLayout' somehow refer to it's previous calculation and then have rounding issues when fitting the same string into its last-calculated width?
OK, the key here was that the text was presented correctly but the size of the view was wrong.
I created a delagate for the NSTextView's layoutManager. The text was being set out 3 times after the NSTextView was added to the NSOutlineView (which seems to be terribly inefficient!). intrinsicContentSize was only being called on the NSTextView after the first two text layouts.
Calling invalidateIntrinsicContentSize from within layoutManager: didCompleteLayoutFor... fixed everything up.
I still don't understand what is happening here though, and why all these methods are being called so many times when stuff is presented. I guess it is to do with the complexity of auto layout and things pushing against each other.
I also don't understand why only some of the calculations were incorrect during the process.
Please comment here if anyone can shine some light on this!

UILabel does not wrap (if I change default font size)

I am working on a simple storyboard prototype. My TableViewController uses Dynamic Prototype as Content.
I have a cell with 4 label of which two will be set in code (the label text). The height of the cell will be calculated in code too. The Line Breaks are set to Word Wrap and everything's working fine with the default values (System 17.0):
see here:
..but if I change the Font Size of the "Fantasy Street..." label it will not break any more instead it just will be cut off!
see here: with System Font 16
Lines are set to 0
Word Wrap is still active
.. I also tried to do it manually in code but no change.
Does anyone have an explanation for that?
****edited:** when I add
myLabel.frame = CGRectMake(t.origin.x, t.origin.y, t.size.width, t.size.height *2);
to the cellForRowAtIndexPath I still see the cut off label. But if I then scroll the table view so the label is outside the viewable area shortly it will be displayed with the complete text when it is visible again.
By the way, I am working with viewTags, so I don't have a dedicated Cell Class e.g. UILabel *myLabel = (UILabel *)[cell viewWithTag:2];
You should check UILabel width; the width should be less than that of the value. Then like this:
(void) viewWillLayoutSubviews {
_landPhoneTips.preferredMaxLayoutWidth = _landPhoneTips.bounds.size.width;
}
I spent hours dealing with this identical problem before finally sorting it out last evening. After changing the font size, you must select the UILabel within the storyboard and select Edit > Size to Fit Content, even if you had already previously done so! In doing so you apparently reset some setting that gets messed up when changing the font size. Once done, the UILabel will wrap as it did previously.

Center Multi-Line Text on UIButton using IB

How do you center text line-by-line in a UIButton using Interface Builder? I am scouring the options and just don't see it. Here's the button:
You can't set the text to be centered in your nib. But you can change the alignment in your code:
- (void)viewDidLoad {
[super viewDidLoad];
self.myButton.titleLabel.textAlignment = UITextAlignmentCenter;
}
I know this is an old question, but I came across it in my own attempt to center the multi-line text of a UIButton in IB. What I found is that by default, when "title" is set to "plain" and "line break" is set to "word wrap" the longest line of the title text is centered and the other lines are left justified to this line (similar to the OP's screen capture).
In order to have all the lines centered properly, "title" needs to be changed to "attributed." This provides many more options to customize the appearance of the title text. Center each of the lines of text (you can now actually change the alignment for each line individually). Also be sure to set "line breaking" to "word wrap" under "more..." above the text. There seems to be a bug with how this line breaking option behaves, at least in Xcode 4.5 at this time, because the text on the button in IB will look incorrect, truncating everything except the first line. It seems the "word wrap" and truncate options are interpreted backwards in IB, but if you run the app it behaves correctly in the simulator.
Actually you can do it in interface builder.
Just set Title to "Attributed" and then choose center alignment.
#from comments : To wrap you need to set Line Break to Character Wrap or Word Wrap.
P.S : This might not render in xcode. But, it will work at runtime.
You can set the center multiline text in UIButton through storyboard.
This is how you make the text have two or more lines.
Set the below key Path at
Identity Inspector --> User defined runtime attributes --> add new key value pair with below
titleLabel.textAlignment - NSNumber - 1
and
titleLabel.numberOfLines - NSNumber - 5 - or use "0" meaning "any number"
It will look like this:
Note that (2016) unfortunately it does not actually show the two or more lines of text in Storyboard (you see only the first one), but it works perfectly when you run in simulator or device.
For IB set Title to "Attributed" and select center alignment (like Alexander Danilov suggested)
But if you want to do it in code using Swift 4:
// center button text
yourButton.titleLabel?.textAlignment = .center
// enable multiline if needed
yourButton.titleLabel?.numberOfLines = 0
Not all options are done using Interface Builder therefore you must do some of them by coding, and usually we do them inside the function viewDidLoad.
To center your text inside a button by code you can use the following:
button1.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
You can use the same technique to align the text to any direction, for example to the left:
button1.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
But this will take the text too much to the left and you might want to have some space before it keeping the alignment to the left, so you add an inset after the aligning code as follows:
button1.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
In this case we are pushing the text from the Y axis by 10 points. I say here points not pixels because as you know Apple uses the points technique to measure distances to be able to adapt easily between normal display and retina display (where retina is 2 times the normal one).
I haven't tried it out yet, but I think a way to do it might be create a CGRect on top of your button, then use it as a frame, create a label, and then you can play with the label, set the textAlignment property to be UITextAlignmentCenter, and set the background color to be clear.
This works with uitableview but I don't know whether that will work for button. Hope this helps.

setting dynamic x coordinate of second lable in xcode objective c

i have a problem that i have two lables,
1) will contain a long text
2) this will be just More button (link)
so i want that when the first text ends then the second label starts right from where it end. but the problem is that the text is variable so i could not find any way to make it dynamic.
example
text of first row
"this is text of first label" "more"
text of second line
"test best" "more"
in the example the rows are of table view and two lables are separated by " so i want second label starting point from where the text in first lable ends
looking forward for some solution
Thanks & Regards!
NSString has some methods to calculate its size when displaying using given font (e.g. sizeWithFont: method) - you can use it to determine text width and place your more button accordingly (someZZZZ parameters must be available on runtime):
CGFloat firstLabelWidth = [firstLabel.text sizeWithFont:firstLabel.font].width;
CGFloat moreX = firstLabel.frame.origin.x + firstLabelWidth + someGap;
moreButton.frame = CGRectMake(moreX, moreY, someWidth, someHeight);
You may need to add some validation for cases when text in first label is too long to fit the screen etc, but in general this code should work.