How to add long text shadow in swift? - ios

I have tried but it was not a long style shadow.
let myString = "Show Your Creativity"
myLabel.layer.masksToBounds = false
myLabel.layer.shouldRasterize = true
myLabel.layer.rasterizationScale = 10
// Create a shadow
let myShadow = NSShadow()
myShadow.shadowBlurRadius = 1
myShadow.shadowOffset = CGSize(width: 40, height: 40)
myShadow.shadowColor = UIColor.gray
// Create an attribute from the shadow
let myAttribute = [ NSAttributedStringKey.shadow: myShadow ]
// Add the attribute to the string
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
// set the attributed text on a label
myLabel.attributedText = myAttrString // can also use with UITextView
Current result ...
I want an effect like given image.

The effect you want to reproduce is not made by adding a shadow to text - as a shadow will follow and extend the curves in the characters.
The image you have linked to seems to be using a linear gradient masked by the tops of the text.
Reproducing this programmatically would be easier done in Sketch/PaintCode using text outlines to form masks and the linear gradient, and then export the result to iOS-compliant code. This will give you resolution independence and a small file size.
Or ship the bitmap image from an illustration app.
Or do you need dynamic text?

Related

Color unselected segment of UISegmentedControl without hiding selection

I wanted to color each segment in my UISegmentedControl in a different color. I used the following Swift code (there were some changes with segmentedControl in iOS 13):
segmentedControl.selectedSegmentTintColor = .white
let col: UIColor = .yellow
var subViewOfSegment: UIView = segmentedControl.subviews[2] as UIView
subViewOfSegment.layer.backgroundColor = col.cgColor
This works in general, one segment is now coloured. However, the selected segment is not shown anymore. The selected segment is supposed to be white, but it seems to be overlaid by the colour. The following image show how it looks when I select each segment from left to right:
I already tried subViewOfSegment.backgroundColor = col instead (same effect) or subViewOfSegment.tintColor = col (no effect at all in iOS 13) but I can't get the colors without hiding the selection. On other posts I only find this answer which doesn't say how to color unselected segments.
iOS 13 (Xcode 13.4) Segment Control 100% Working
mySegmentedControl.selectedSegmentTintColor = .white
let subView = mySegmentedControl.subviews[1] as UIView
subView.layer.backgroundColor = UIColor.yellow.cgColor
let subViewOfSegment: UIView = segmentedControl.subviews[1] as UIView
subViewOfSegment.backgroundColor = UIColor.red
subViewOfSegment.layer.zPosition = -999
> above solution is not proper but one of the way you can achieve it. you need manage one more label while select segment is red"`
`
You can do it by using the following code:
init(){
UISegmentedControl.appearance().backgroundColor = .yellow
}
var body: some View{
your code…
}
if you want to change the selected segment’s color, you can use:
UISegmentedControl.appearance().selectedSegmentTintColor = .blue
to change it. I hope these codes can help you :D
For detail, you can visit here.
Using Apple APIs, you could try drawing an image with the 4 colors and set that as background using setBackgroundImage(_:for:barMetrics:).
Here's some code to draw a 40x10 image with just one color to get you started:
let rect = CGRect(origin: CGPoint(x: 0, y:0), size: CGSize(width: 40, height: 10))
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
To get an image with your four colors, you need to repeat context.setFillColor(<varying color>) and context.fill(<varying rect>) four times.
The image should stretch, which means you it only needs to have four equally sized colored rectangles; you don't need to create it in the exact size of the segmented control.
You can then set the selected segment color using the selectedSegmentTintColor property.

Having textfield background transparent, but placeholder text opaque

Is it possible to have a textfield's placeholder text opaque while its background is set to be transparent, eg. if I choose some background color, and set alpha to some value, like this:
the placeholder text becomes transparent as well. But I want it opaque. So, is this achievable, preferably using storyboard ? Or at least through the code.
If it is unclear what I am trying to achieve, just let me know , and I'll post an image with an example.
You can set the color's transparency instead of the UITextField's alpha. Open the color drop down and select "Other". A color picker will open up. At the bottom of the color picker you can change the opacity.
In Swift you can obtain the placeholder element with:
let placeHolder = textField.value(forKey: "placeholderLabel") as! UILabel
placeHolder.textColor = .blue
placeHolder.isOpaque = true
and make all customizations you prefeer..
You can set the UITextView.background = .clear
I'm providing a little more code so it'll be easier to setup it programmatically, this is a ready-to-use sample.
private lazy var textField: UITextView = {
let textField = UITextView()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = UIFont.systemFont(ofSize: 14)
textField.isEditable = false
textField.textAlignment = .center
textField.isScrollEnabled = false
textField.backgroundColor = .clear // this is the line that answer you question
textField.text = """
People with these symptoms may have COVID-19:
Fever of chills, cough, shortness of breath or
difficulty breathing, fatigue, muscle or body aches,
headache, new loss of taste and smell, sore throat,
congestion or runny nose, nausea or vomiting,
diarrhea
"""
return textField
}()
Just remember to set the constraints and add the subview into your superview .addSubview(TextField)

iOS - image attachment for the text view changes attributes

I've a UITextView described as follows with the given attributes:
lazy var inputTextView: UITextView = {
let tv = UITextView()
tv.backgroundColor = .white
tv.textContainerInset = UIEdgeInsetsMake(12, 12, 12, 12) // Posicionamento do texto
let spacing = NSMutableParagraphStyle()
spacing.lineSpacing = 4
let attr = [NSParagraphStyleAttributeName : spacing, NSFontAttributeName: UIFont.systemFont(ofSize: 16), NSForegroundColorAttributeName: UIColor.blue]
tv.typingAttributes = attr
return tv
}()
Everything works as expected until I attach an image to the UITextView.
The image gets inserted in the desired position but after its inserted it overrides my textView attributes.
The text becomes small and in a different color than the attributes I've implemented in its declaration.
I'm attaching the image as follows:
let att = NSTextAttachment()
att.image = image
let attrString = NSAttributedString(attachment: att)
self.inputTextView.textStorage.insert(attrString, at: self.currentCursorLocation)
What's causing this issue?
I've even tried to reenforce its attributes whenever I insert an UIImage to its content.
I've tried the following when adding the image:
let att = NSTextAttachment()
att.image = image
let attrString = NSAttributedString(attachment: att)
self.inputTextView.textStorage.insert(attrString, at: self.currentCursorLocation)
let spacing = NSMutableParagraphStyle()
spacing.lineSpacing = 4
let attr = [NSParagraphStyleAttributeName : spacing, NSFontAttributeName: UIFont.systemFont(ofSize: 16), NSForegroundColorAttributeName: UIColor.blue]
self.inputTextView.typingAttributes = attr
And it still doesn't change its attributes.
Whats causing this issue? Any tip?
Thanks
Edit
As suggested here's how I'm setting the cursor position
func textViewDidChange(_ textView: UITextView) {
currentCursorLocation = textView.selectedRange.location
}
I do this to insert the image at the current location of the text blinking cursor
[Edit: Unfortunately this does not solve Ivan's problem - I leave the answer because it is interesting detail for those who do not understand Unicode character encoding].
String range specification is non-intuitive due to the subtleties of Unicode. I expect your issue is that the cursor position at which you are inserting your image is not where you think it is relative to the text and you are inserting the image at a Unicode scalar position that is not between Unicode code points, such that you are corrupting a unicode code. To understand why this can happen, see this Apple article.
Strings in Swift 2
I would suggest using the following notation when specifying string ranges (taken from this Stack Overflow answer: NSAttributedString and emojis: issue with positions and lengths).
// Convert to NSRange by computing the integer distances:
let nsRange = NSRange(location: text.utf16.distance(from: text.utf16.startIndex, to: from16),
length: text.utf16.distance(from: from16, to: to16))
However without seeing how you set your cursor position, it is not possible for me to be sure this is the source of your problem. [Update: thanks for updating the question to show the cursor position - we got there in the end but for others, note, after setting the cursor position this way (which would have been fine), he was incrementing it by 1, which meant the issue I have referred to about Unicode scalars versus code points was in fact the issue].

Multi-line editable piece of text: editable UILabel?

I am trying to create a large plot of editable text but there seems to be 1 option: using a small UITextField.
I know UILabels can be big and wide but I do not know how to make an editable UILabel.
I experimented with UILabel properties and the .layer method but nothing seems to be really working. Anybody have a recommendation as to what I should do?
To summarize, I am looking for a multi-line editable piece of text.
UITextView for the win!!
UITextViews allow for multiple line manipulation of texts and if you use the UITextViewDelegate, it can provide for methods that allow specific things when the textView is clicked on, etc...!
With a UITextView you can provide a specific amount of lines (if you only want 3, you can specify it) and also provide hyperlinks, if need be.
Here is an example I have (changed a little) to show an example for ya...
let textBox:UITextView = UITextView(frame: CGRect(x: firstBox.frame.width*0, y: firstBox.frame.height*0.375, width: firstBox.frame.width*1, height: firstBox.frame.height*0.5))
textBox.backgroundColor = UIColor.clearColor()
let websiteName = "http://stackoverflow.com/posts/38035564"
textBox.text = "SO is an awesome coding site! Please visit\n\(websiteName)"
//No need to set number of lines, it will auto set to as many as needed!
textBox.editable = false
textBox.selectable = true
//Register the hyperlink
textBox.dataDetectorTypes = UIDataDetectorTypes.All
textBox.textColor = UIColor.grayColor()
//Change only the hyperlink part
let textRange = NSMakeRange(textBox.text.characters.count-websiteName.characters.count, websiteName.characters.count)
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.Center
let attributedText = NSMutableAttributedString(string: textBox.text, attributes: [NSFontAttributeName:UIFont(
name: (textBox.font?.fontName)!,
size:13/15*fontSize)!,
NSParagraphStyleAttributeName: style])
attributedText.addAttribute(NSUnderlineStyleAttributeName , value:NSUnderlineStyle.StyleSingle.rawValue, range: textRange)
textBox.attributedText = attributedText
firstBox.addSubview(textBox)

Vertically centering text in an irregularly shaped UITextView

I'm working on rendering text in the center of a circular or oval shaped region. Ideally, I'd like to center the text vertically and horizontally, but have it flow naturally at the boundaries of the region.
I've found an approach using a UITextView and NSTextContainer with exclusion paths that does a good job of laying out the text at a given vertical offset, centered horizontally (code below). However, I'm not sure how to achieve vertical centering.
The approaches I've seen all suggest adjusting the contentInset of the text view after the text has been laid out and the final height is known. (For example, this question) However, in the case of an irregularly shaped region, the height of the text after it's been laid out will depend on where in the region layout starts from.
An approach I've considered is to retry the layout process until a satisfactory layout is achieved. Has anyone had success with this kind of approach? One challenge here is that I haven't worked out how to determine whether all the text has been rendered within the view (i.e. whether there is enough space to lay out all the text) --- is there a way to query whether all content has been 'rendered' when using a UITextView?
Finally: this is just for displaying text --- there is no need to allow a user to edit the content of the view. Would CoreText perhaps be a better approach in this case?
I'm fairly new to iOS development, so if there's anything outrageous I'm doing, that would also be helpful to know!
Thanks!
let boundingRect = CGRect(...)
let textView = UITextView(frame: boundingRect)
textView.editable = false
view.addSubview(textView)
let verticalInset:CGFloat = <some value>
let width = boundingRect.width
let height = boundingRect.height
let textBounds = CGSize(width: width, height:height - 2*verticalInset)
textView.bounds = textBounds
let exclusionRect = CGRect(x:0, y:-verticalInset, width:width, height:height)
let textRegion = UIBezierPath(ovalInRect: exclusionRect)
let exclusionPath = UIBezierPath(rect:exclusionRect)
exclusionPath.appendPath(textRegion.bezierPathByReversingPath())
textView.textContainer.exclusionPaths = [exclusionPath]
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .Center
let attributes = [NSParagraphStyleAttributeName: paragraphStyle]
let formattedContent = NSAttributedString(string: "....", attributes:attributes)
textView.attributedText(formattedContent)
Here's an image to help visualize the above, if helpful:
If you lay out text in a rectangular area, it's easy to center it vertically afterwards by shifting it upwards or downwards. If the area is not rectangular, as in your case, this is not possible, as the text doesn't necessarily fit in the shape after you shift it vertically.
I believe that a solution to this is always an iterative method. For example: Render the text in the shape with increasing insets, and find the largest inset so that the text fits in the resulting shape.
To do this with Core Text, you can create a CTFrame for the text layout like this:
let text:NSAttributedString = ... // The text to render
let path:CGPath = ... // Bounds the shape in which the text is rendered
let framesetter = CTFramesetterCreateWithAttributedString(text)
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)
With this frame, you can check if the whole string has been rendered:
let range = CTFrameGetVisibleStringRange(frame)
if text.length == range.length {
// Fits
}
And to render the text:
let ctx:CGContext = ...
CTFrameDraw(renderFrame, ctx)
If you need more details about the rendered text, you can use CTFrameGetLines to get the CTLines from frame. Then, using CTFrameGetLineOrigins and CTLineGetBoundsWithOptions with the option UseGlyphPathBounds or UseOpticalBounds, you can calculate the optical bounds for the single lines. This allows for a more precise vertical adjustment. (Brace yourself for some linear algebra, the coordinate system transformations that are needed to use CoreText with CoreGraphics directly can be a bit tedious).
I can't offer a fantastic answer here.
However, I've just tried out YYText. This has a "YYLabel" that supports vertical text alignment, and exclusion paths. Take a look at their Demo project, specifically the 'TextAttributes1' row in their tableview that demonstrates.

Resources