How do I wrap truncated text in a SwiftUI Text with quotes? - text

I want to achieve exactly the same effect as specified by this question, except in SwiftUI. That is, I want a Text object set to .lineLimit(3).truncationMode(.tail) that will render the text, including the trailing ellipsis, inside proper typographical quotation marks, like so: “It was the best of times, it was...”
The best solution for the problem in UIKit won't transfer over to SwiftUI in the obvious way:
Text("“\(transcript)").lineLimit(3).truncationMode(.tail) + Text("”") // error
Xcode complains that "Cannot convert value of type 'some View' to expected argument type 'Text'", as the type of the return of lineLimit(_:) and truncationMode(_:) aren't Text and therefore concatenation with + isn't allowed. I couldn't get any combination of casts or wrapping various parts in #ViewBuilders to work, which makes sense. They're different types.
Is there any way of doing this without basically re-implementing .lineLimit(_:) myself?

The best way we found so far is to kind of define the problem away, which maybe turns out to be a better typographic solution anyway:
HStack(spacing: 0) {
VStack {
Text("“")
Spacer()
}
HStack {
Text("Very long text to be truncated goes here")
.lineLimit(2)
}
VStack {
Spacer()
Text("”")
}
}
This puts the quote characters to the left and right of the text, which is kind of what you want for a "pull quote" anyway.
Edit: I just realised that unfortunately this has an issue—if the text is too short to be truncated, you end up with blank space before the quotes: “Like this. ”

Related

Jetpack Compose - trying to mimic Intellij's code design into a Text block

I have a UI that I am creating that replicated Android Studio's (IntelliJ) design system for text as code. What I am trying to achieve is a text that changes it's color multiple times when certain words or patterns happen, such as Kotlin keywords (orange text), extension methods(yellowish text) and variables (purple text).
What do I mean? the following functions text design is what I want to achieve, and they are examples of using all 3 colors -
So I tried to implement it using a buildAnnotatedString { } block and things got A) very messy and B) didn't even reach my goal. Here is my current code of trying to implement keywords and extensions -
// A dummy function that represents only the text part of my entire screen, everything else is irrelevant for the question
#Composable
fun TextAsIntellij(solution : String) {
Text(text = buildAnnotatedString {
val kotlinKeywords = mutableListOf("fun", "val", "var", "if", "return", "null", ",", "class", "while", "break", "continue")
val kotlinExtensions = mutableListOf("forEachIndexed") // will add more as needed
solution.split(" ", ".").forEach { string ->
if (kotlinKeywords.contains(string)) {
withStyle(
style = SpanStyle(
color = Orange
)
) {
append(string)
append(' ')
}
return#forEach
} else if (kotlinExtensions.contains(string)) {
withStyle(
style = SpanStyle(
color = Yellow
)
) {
append(string)
append(" ")
}
return#forEach
}
append(string)
append(' ')
}
})
}
The results in the UI are the following -
As you can see, in the first question I have a problem that before an extension I put a redundant space because I used split() method to identify all words by spaces and dots. For the 2nd picture, I don't yet have a proper solution for identifying varibles from an "object", because I have already split my String using spaces and dots, so diving deeper into another split would make my code really inefficient and I though better can be done. 😁
Hopefully someone has a good idea...any Jetbrains spy in the crowd? ;)

How to capitalize the first word of a sentence in Ionic 4?

I'm trying to capitalize the first word of a sentence in Ionic 4 INPUT, but doesnt work after make many tests on CSS, ion-text-capitalize.... What is the easiest way to do it? Thank you so much
Writing an ionChange event handler for ion-textarea
I have deleted my previous answer and edited it to some working code.
I personally don't think this is a good solution because these types of things end up buggy or slow.
There is an ion-event that you can write against.
I've written a simple snippet which replaces the first letter with an uppercase letter.
(template-test.page.html)
<ion-content>
<ion-title>Text Area Test</ion-title>
<ion-item>
<ion-textarea [(ngModel)]="someAutoFormattedInput" (ionChange)="onAutoFormatChanged()"></ion-textarea>
</ion-item>
</ion-content>
(template-test.page.ts)
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-textarea-test',
templateUrl: './textarea-test.page.html',
styleUrls: ['./textarea-test.page.scss'],
})
export class TextareaTestPage implements OnInit {
someAutoFormattedInput: string;
constructor() { }
ngOnInit() {
}
onAutoFormatChanged() {
this.someAutoFormattedInput = this.setFirstLetterToUppercase(this.someAutoFormattedInput);
}
private setFirstLetterToUppercase(string:string):string {
// https://dzone.com/articles/how-to-capitalize-the-first-letter-of-a-string-in
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
Why I don't like this
As I said, these types of things end up buggy and laggy. This is both, but I've presented it as it might be "good enough" for your problem.
Buggy - for example, if you put a new first letter in, it will uppercase it, but the original letter in slot two will be uppercase. It's not easy to assume that you should lowercase everything else so writing rules to manage this is... buggy.
What I mean is:
Buggy
Uggy (delete the first letter, works good)
BUggy (add a new first letter, now we have a messy input)
There are probably other edge-cases lurking.
Laggy- This does string operations on the whole input, which could be very long, depending on what you're working with. This has the potential to be expensive and slow.
What I would do
I would let the user enter whatever they want and then when the data is saved I would do this formatting / cleaning up of the data.
Or I would use a custom pipe just to format the data as its being displayed and then store the original user input unaltered.
You can implement it in a different way like below:
1. Via InLine Style:
<p style="text-transform: capitalize;">This will be transformed to capitalize all words, including both parts of this hyphenated word: double-parked.</p>
2. Via CSS class:
.capitalize { text-transform: capitalize; }
<p class="capitalize">This will be transformed to capitalize all words, including both parts of this hyphenated word: double-parked.</p>
Hope this will helps!
Use autocapitalize property of textarea field, use autocapitalize="true", e.g.
<ion-textarea formControlName="description"
rows="5" autocapitalize="true">
It will auto capitalize first letter of every sentence.

UITextView: how to set the AttributedText with an empty String?

I have an UItextView and i set the AttributedText at the starting of the app so with en empty text because user didn't yet fill anything. The problem is that with an empty String the AttributedText seam to not apply for the new text i will enter. how to do ?
As far as I know there is no straight solution for that. Setting AttributedText with "" doesn't work.
However you can do easy fix:
if let text = field.attributedText?.string {
//normal way
} else {
field.font = ...
field.fontColor = ...
//sorry, no shadow and other nice tricks
}
Of course you could implement delegate to text field and adjust attributes when textFieldDidChange, but that doesn't work well with typing in Chinese language where letter can be composed from multiple characters so I couldn't use that.

Locating and removing an esc sequence objective-c

I'm trying to remove an escape sequence (\p) from selected strings (for reuse in other places in my code). I want to create a separate object that holds that cleaned up string. Like this
(pseudo code)
NSString *stringWithEscSequence = anEdgewoodSecretPlace.placeName;
NSString *stringWithoutEscSequence = [removeTheEscSequenceP stringWithEscSequence];
(where removeTheEscSequenceP is what I'm trying to figure out how to do)
....
I played with NSString formats and I found related questions at
Position of a character NSString
and
how to replace one substring with another in Objective C?
I can break out the substring that follows the escape sequence use ideas from the second posting. So I thought then I should try to locate the actual index of the escape sequence. But I can't figure out how to do that. Also I think there is probably a less messy, more straightforward way to do this, that I'm not thinking of.
I was playing around with the first posting ideas, though I knew that I was in a kind of primitive mode. But as soon as I attempt to replace "ABCD" with "\". I get an error 'Missing terminating """ = except if I put the ABCD string back in I'm not missing any quotes. I think it's because of the special nature ofo the \ character.
Even if I could figure out how to search for the "\" and then a "p" following that, I don't think that's really the best way to approach this
NSCharacterSet *charSet = [NSCharacterSet characterSetWithCharactersInString:#"ABCD"];
NSRange range2 = [testCleanString rangeOfCharacterFromSet:charSet];
if (range2.location == NSNotFound) {
NSLog (#"Didn't find esc sequence");
} else {
NSLog (#"range.location is %lu",(unsigned long)range2.location );
}
You could try with this:
NSString* stringWithoutEscSequence = [stringWithEscSequence stringByReplacingOccurrencesOfString:#"stringToRemove" withString:#"replacement"];
If you want to replace multiple characters you could do:
NSCharacterSet* toRemove = [NSCharacterSet characterSetWithCharactersInString:#"\n\p\t"];
NSString* stringWithoutEscSequence = [[stringWithEscSequence componentsSeparatedByCharactersInSet:toRemove] componentsJoinedByString: #""];

Detect a change in a rich text field's value in SPItemEventReceiver?

I currently have an Event Receiver that is attached to a custom list. My current requirement is to implement column level security for a Rich Text field (Multiple lines of text with enhanced rich text).
According to this post[webarchive], I can get the field's before and after values like so:
object oBefore = properties.ListItem[f.InternalName];
object oAfter = properties.AfterProperties[f.InternalName];
The problem is that I'm running to issues comparing these two values, which lead to false positives (code is detecting a change when there wasn't one).
Exhibit A: Using ToString on both objects
oBefore.ToString()
<div class=ExternalClass271E860C95FF42C6902BE21043F01572>
<p class=MsoNormal style="margin:0in 0in 0pt">Text.
</div>
oAfter.ToString()
<DIV class=ExternalClass271E860C95FF42C6902BE21043F01572>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt">Text.
</DIV>
Problems?
HTML tags are capitalized
Random spaces (see the additional space after margin:)
Using GetFieldValueForEdit or GetFieldValueAsHTML seem to result in the same values.
"OK," you say, so lets just compare the plain text values.
Exhibit B: Using GetFieldValueAsText
Fortunately, this method strips all of the HTML tags out of the value and only plain text is displayed. However, using this method led me to discover additional issues with whitespace characters:
In the before value:
Sometimes there are additional newline characters.
Sometimes spaces are displayed as non-breaking spaces (ASCII char code 160)
Question:
How can I detect if the user changed a rich text field in an event receiver?
[Ideal] Detect any change to HTML or text or white space
[Acceptable] Detect changes to text or white space
[Not so good] Detect changes to text characters only (strip all non-alphanumeric characters)
What happens if you set the ListItem field with the new value and read it back out? Does that give the same formatting?
object oBefore = properties.ListItem[f.InternalName];
properties.ListItem[f.InternalName] = properties.AfterProperties[f.InternalName]
object oAfter = properties.ListItem[f.InternalName];
//dont update
properties.ListItem[f.InternalName] = oBefore;
I would probably try something between choices 2 and 3:
bool changed =
valueAsTextBefore != valueAsTextAfter ||
0 != string.Compare(
oBefore.ToString().Replace(" ", ""),
oAfter.ToString().Replace(" ", ""),
true);
The left half checks if the text (including case) has changed while the right half checks if the tags or attributes have changed. Very kludgy, but should fit your case.
The only other thing I can think of is to run an XML transform on the HTML in order to standardize on case and spacing. But not only does that seem like overkill, but it assumes the HTML will always be well formed.
I'm currently testing a combination approach: GetFieldValueAsText and then stripping out all characters except alphanumeric/punctuation:
static string GetRichTextValue(string value)
{
if (null == value)
{
return string.Empty;
}
StringBuilder sb = new StringBuilder(value.Length);
foreach (char c in value)
{
if (char.IsLetterOrDigit(c) || char.IsPunctuation(c))
{
sb.Append(c);
}
}
return sb.ToString();
}
This only detects changes to the text of a rich text field but seems to work consistently.

Resources