Adding text over UIImage in swift - programmatically - swift

I'm trying to add text over UIView just like on instagram Stories.
I'm using this function that draws textViews over a a view the size of the screen frame
func passingAndDrawingTextViews(textViews : [UITextView], viewPassed : UIView, inImage : UIImage) -> UIImage{
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(viewPassed.frame.size, false, scale)
viewPassed.draw(CGRect(x: 0, y: 0, width: viewPassed.frame.width, height: viewPassed.frame.height))
//adding each textView
textViews.forEach { (textView) in
let textColor = textView.textColor
let textFont = UIFont(name: textView.font!.fontName, size: textView.font!.pointSize)
let textAlignment = textView.textAlignment
//alignment of the text
let paragraphStyle: NSMutableParagraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = textAlignment
let textFontAttributes = [
NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.paragraphStyle : paragraphStyle,
]
let rect = CGRect(x: textView.frame.minX, y: textView.frame.minY, width: textView.frame.width, height: textView.frame.height)
textView.text.draw(in: rect, withAttributes: textFontAttributes as [NSAttributedString.Key : Any])
}
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
The text is drawn, the problem is that It isn't in the exact point the textview is positioned in the viewPassed. In particular the text that is drawn is a little bit upper-left in respect of the original textView.
I was wondering why this could happen and how to solve it, I'm wrapping my head around but it seems I can't find a solution.

Related

How to calculate boundRect correctly for NSAttributedString When apply shadow

I tried apply shadow effect to UILabel using NSAttributeString. I want to change the size of UILabel whenever change shadow offset. However, it doesn't calculate correct frame and ignore shadow offset. How can I achieve?
let textView = UILabel(frame: CGRect(x: 20, y: 100, width: 300, height: 50))
textView.layer.borderColor = UIColor.red.cgColor
textView.layer.borderWidth = 1
textView.numberOfLines = 0
textView.text = "hyHgjpQ\nhyHgjpQ\nhyHgjpQ"
textView.font = UIFont(name: "DamascusSemiBold", size: 40)
textView.textColor = .white
textView.textAlignment = .center
self.view.addSubview(textView)
var textAttributes: [NSAttributedString.Key: AnyObject] = [:]
// font name & size
let font = UIFont(name: "HelveticaNeue", size: 50)
textAttributes[NSAttributedString.Key.font] = font
// shadow
let shadow = NSShadow()
shadow.shadowBlurRadius = 10
shadow.shadowOffset = CGSize(width: 10, height: 30)
shadow.shadowColor = UIColor.red
textAttributes[NSAttributedString.Key.shadow] = shadow
textView.attributedText = NSAttributedString(string: textView.text ?? "", attributes: textAttributes)
let bound = textView.text!.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: textAttributes,
context: nil)
textView.bounds = bound
Result

MacOS: Add a text overlay to an image in Swift

I've written a small extension for UIImage, which adds a text to an image:
extension UIImage {
func addTextToImage(textToAdd: String) -> UIImage {
let textColor = UIColor.white
let textFont = UIFont(name: "Snell Roundhand", size: 40)!
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(self.size, false, scale)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = NSTextAlignment.center
var textFontAttributes = [
NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.paragraphStyle: paragraphStyle
] as [NSAttributedString.Key : Any]
self.draw(in: CGRect(origin: CGPoint.zero, size: self.size))
let textFrame = CGPoint(x: self.size.width/4, y: self.size.height/4)
let rect = CGRect(origin: textFrame, size: CGSize(width: self.size.width/2, height: self.size.height/2) )
text.draw(in: rect, withAttributes: textFontAttributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
The result should look like this:
Now I want to do the same thing for NSImage to run on MacOS.
Any ideas how to do this?
All similar questions on Stackoverflow are either very old, unanswered or in Objective C
Found a solution:
extension NSImage {
func addTextToImage(drawText text: String) -> NSImage {
let targetImage = NSImage(size: self.size, flipped: false) { (dstRect: CGRect) -> Bool in
self.draw(in: dstRect)
let textColor = NSColor.white
let textFont = NSFont(name: "Snell Roundhand", size: 36)! //Helvetica Bold
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = NSTextAlignment.center
var textFontAttributes = [
NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: textColor,
] as [NSAttributedString.Key : Any]
let textOrigin = CGPoint(x: self.size.height/3, y: -self.size.width/4)
let rect = CGRect(origin: textOrigin, size: self.size)
text.draw(in: rect, withAttributes: textFontAttributes)
return true
}
return targetImage
}
}

Clipped text on UILabel

I'm using custom a font for UILabel but there is a strange problem, for some characters the text is being clipped:
The label should be like this (label frame has enlarged manually):
This is the code to add the label to the gray view:
let string = "کیخسروی"
let font = UIFont(name: "IranNastaliq", size: 30)!
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
paragraphStyle.alignment = .center
let attributes: [NSAttributedStringKey: Any] = [
.font: font,
.paragraphStyle: paragraphStyle
]
let mutabbleAttributedString = NSMutableAttributedString(string: string, attributes: attributes)
let rectSize = mutabbleAttributedString.size()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: rectSize.width, height: rectSize.height))
label.font = font
label.attributedText = mutabbleAttributedString
label.backgroundColor = UIColor.yellow
label.textAlignment = .center
let size = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
let width = label.sizeThatFits(size).width
let height = label.sizeThatFits(size).height
let frame = CGRect(x: 0, y: 0, width: width, height: height)
label.frame = frame
self.myView.addSubview(label)
label.center = self.myView.center
Seems sizeThatFits() does not calculate the bounds correctly.
I have to mention this clipping only happens while using some custom fonts.

manually setting titleView of navigationItem is not alligned vertically

I am using this func inside a UIViewController extension to add a title that adjusts font to fit width.
extesion UIViewController {
func setTitleDifferentSizes(title: String){
self.title = title
guard let navigationBarHeight: CGFloat =
self.navigationController?.navigationBar.frame.height else{
return
}
let tlabel = UILabel(frame: CGRect(x: 0.0, y: 0.0, width:
200.0, height: navigationBarHeight))
tlabel.text = self.title
tlabel.textColor = UIColor.white
tlabel.font = font24
tlabel.backgroundColor = UIColor.clear
tlabel.adjustsFontSizeToFitWidth = true
self.navigationItem.titleView = tlabel
}
}
I took this solution from this SO question and changed it a little bit:
How to resize Title in a navigation bar dynamically
Now the issue I have is that the text of the title is not aligned vertically to the other navigation bar items, as you can see in the images, I show one where I just setup the title without using the above method, and the text there cannot fit but it is aligned properly, and the other image is using the method above where the text fits but it is not aligned.
try this:-
func setTitleDifferentSizes(title: String){
self.title = title
guard let navigationBarHeight: CGFloat =
self.navigationController?.navigationBar.frame.height else{
return
}
let attributedString = NSMutableAttributedString(string: title)
let myAttribute = [ NSForegroundColorAttributeName: UIColor.white ,NSFontAttributeName: font24]
attributedString.addAttributes(myAttribute, range: NSRange(location: 0, length: attributedString.string.characters.count))
attributedString.addAttributes([NSBaselineOffsetAttributeName:6.0], range: NSRange(location: 0, length: title.characters.count)
)
let tlabel = UILabel(frame: CGRect(x: 0.0, y: 0.0, width:
200.0, height: navigationBarHeight))
tlabel.attributedText = attributedString
tlabel.backgroundColor = UIColor.clear
tlabel.adjustsFontSizeToFitWidth = true
tlabel.minimumScaleFactor = 0.2
tlabel.textAlignment = .center
self.navigationItem.titleView = tlabel
}
if you want to adjust the position of text please change the float value of NSBaselineOffsetAttributeName to set the vertical alignment.

UISlider making it square not rounded

why does it do this? how can i change it? and is there anyway to change the slider so the progress slider is not rounded but square?
http://imgur.com/F9Ye24v
I would prefer it to be square, the code is just a normal UI slider implementation. I only edited the
override func trackRectForBounds(bounds: CGRect) -> CGRect {
return CGRectMake(0, 0, self.frame.width, 25)
}
to make the bar taller
You can set the minimum and maximum track image.
Just use an image (rectangle without corner radius) with a width of 1px and height of 25px.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UISlider_Class/#//apple_ref/doc/uid/TP40006798-CH3-SW21
Sample:
slider.setMinimumTrackImage(UIImage(named: "back"), forState: UIControlState.Normal)
slider.setMaximumTrackImage(UIImage(named: "back"), forState: UIControlState.Normal)
The back image is just a blue square image of size 25px.
The back Image: (just copy and paste)
slider.setMaximumTrackImage(UIImage.imageWith(color: .yellow), for: .normal)
slider.setMinimumTrackImage(UIImage.imageWith(color: .cyan), for: .normal)
UIImage extension:
static func imageWith(color: UIColor, size: CGSize? = nil) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size?.width ?? 1, height: size?.height ?? 1)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor)
context?.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Simple
slider.setMinimumTrackImage(getImageWithColor(color: .blue, size: slider.frame.size), for: .normal)
func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}