How to reduce the quality of the text inside UITextView with Swift
You can drop the quality of the layer by making it rasterized and reducing the scale of it:
let badQualityRatio: CGFloat = 4
textView.layer.shouldRasterize = true
textView.layer.rasterizationScale = UIScreen.main.scale/badQualityRatio
Result:
you can set the rasterizationScale
any number between 0 and UIScreen.main.scale
UITextView Fonts are bluring after Zoom IN
Finally, I was able to fix this issue using contentScale , it should be update for UIView and layer of the UIView and subviews and sub-layers of the particular UIView. otherwise, it will not be working.
func scaleView( view: UIView, scale: CGFloat ){
view.contentScaleFactor = scale
for vi in view.subviews {
scaleView(view: vi, scale: scale)
}
}
func scaleLayer( layer: CALayer, scale: CGFloat ){
layer.contentsScale = scale
if layer.sublayers == nil {
return
}
for la in layer.sublayers! {
scaleLayer(layer: la, scale: scale)
}
}
We should call the above two methods for UITextView in scrollViewDidEndZooming (which is inherited from UIScrollViewDelegate) method.
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
scaleView(view: textView, scale: scale)
scaleLayer(layer: textView.layer, scale: scale)
}
But for the zoom-out feature, please don't set scale value directly then quality will lose. So need to keep the threshold value, it should be calculated according to your usage.
Scale UITextView and maintain text clarity
You need to change the UIFont
size
when you scale up your UITextView
.
Otherwise your text
will get blurry because you are scaling up the actual frame of the UITextView
together with the text
but the current UIFont
is still a small size
so you will see it as pixelated and blurry.
EDIT:
Calculate font size:
Resize font size to fill UITextView?
iOS TextView is saved blurry when scaled
Here's a quick example using a UIView
extension from this accepted answer: https://stackoverflow.com/a/51944513/6257435
We'll create a UITextView
with a size of 240 x 129
. Then add 4 buttons to capture the text view at 1x, 2x, 5x and 10x scale.
It looks like this when running:
and the result...
At 1x
scale - 240 x 129 pixels:
At 2x
scale - 480 x 258 pixels:
At 5x
scale - 1200 x 645 pixels (just showing a portion):
At 10x
scale - 2400 x 1290 pixels (just showing a portion):
The extension:
extension UIView {
func scale(by scale: CGFloat) {
self.contentScaleFactor = scale
for subview in self.subviews {
subview.scale(by: scale)
}
}
func getImage(scale: CGFloat? = nil) -> UIImage {
let newScale = scale ?? UIScreen.main.scale
self.scale(by: newScale)
let format = UIGraphicsImageRendererFormat()
format.scale = newScale
let renderer = UIGraphicsImageRenderer(size: self.bounds.size, format: format)
let image = renderer.image { rendererContext in
self.layer.render(in: rendererContext.cgContext)
}
return image
}
}
Sample controller code:
class TextViewCapVC: UIViewController {
let textView = UITextView()
let resultLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
// add a stack view with buttons
let stack = UIStackView()
stack.axis = .vertical
stack.spacing = 12
[1, 2, 5, 10].forEach { i in
let btn = UIButton()
btn.setTitle("Create Image at \(i)x scale", for: [])
btn.setTitleColor(.white, for: .normal)
btn.setTitleColor(.lightGray, for: .highlighted)
btn.backgroundColor = .systemBlue
btn.tag = i
btn.addTarget(self, action: #selector(gotTap(_:)), for: .touchUpInside)
stack.addArrangedSubview(btn)
}
[textView, stack, resultLabel].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// text view 280x240, 20-points from top, centered horizontally
textView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
textView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
textView.widthAnchor.constraint(equalToConstant: 240.0),
textView.heightAnchor.constraint(equalToConstant: 129.0),
// stack view, 20-points from text view, same width, centered horizontally
stack.topAnchor.constraint(equalTo: textView.bottomAnchor, constant: 20.0),
stack.centerXAnchor.constraint(equalTo: g.centerXAnchor),
stack.widthAnchor.constraint(equalTo: textView.widthAnchor),
// result label, 20-points from stack view
// 20-points from leading/trailing
resultLabel.topAnchor.constraint(equalTo: stack.bottomAnchor, constant: 20.0),
resultLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
resultLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
let string = "Test"
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.blue,
.font: UIFont.italicSystemFont(ofSize: 104.0),
]
let attributedString = NSMutableAttributedString(string: string, attributes: attributes)
textView.attributedText = attributedString
resultLabel.font = .systemFont(ofSize: 14, weight: .light)
resultLabel.numberOfLines = 0
resultLabel.text = "Results:"
// so we can see the view frames
textView.backgroundColor = .yellow
resultLabel.backgroundColor = .cyan
}
@objc func gotTap(_ sender: Any?) {
guard let btn = sender as? UIButton else { return }
let scaleFactor = CGFloat(btn.tag)
let img = textView.getImage(scale: scaleFactor)
var s: String = "Results:\n\n"
let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fName: String = "\(btn.tag)xScale-\(img.size.width * img.scale)x\(img.size.height * img.scale).png"
let url = documents.appendingPathComponent(fName)
if let data = img.pngData() {
do {
try data.write(to: url)
} catch {
s += "Unable to Write Image Data to Disk"
resultLabel.text = s
return
}
} else {
s += "Could not get png data"
resultLabel.text = s
return
}
s += "Logical Size: \(img.size)\n\n"
s += "Scale: \(img.scale)\n\n"
s += "Pixel Size: \(CGSize(width: img.size.width * img.scale, height: img.size.height * img.scale))\n\n"
s += "File \"\(fName)\"\n\nsaved to Documents folder\n"
resultLabel.text = s
// print the path to documents in debug console
// so we can copy/paste into Finder to get to the files
print(documents.path)
}
}
Related Topics
Swift: Download Image from Internet and Cache Them Doesn't Work Properly. Need Suggestions
Swiftui How to Invoke the Function and Change View from Other Page
Access Enum Associated Value as Optional
Ios8: Auto-Layout and Gradient
Sending Variables with a Segue
Swift - Protocol as Type as Target of Button Action
How to Mimic the Description Output
Xcode 7.1 Beta: Content of File Error
How to Make a Variable from a String
Checkboxes in Uitableview State Persistence
Cicolorcontrols & Uislider W/ Swift 4
How to Conditionally Define a Podspec Property Depending on Static or Dynamic Usage
Xcode Swift 3: Timer and Segue View Controller Error
How to Notify a Queue in Swift (Gcd)
Swift Uikit Dynamics Add Collision Boundary. After Rotation Does Not Work Correctly
Swift: How to Invalidate a Timer If the Timer Starts from a Function
What Does an Exclamation Mark in a Property in Swift Language