I’m utilizing textual content package 2 in UITextView by making usingTextLayoutManager property true. I’m attempting so as to add left and proper indents in NSAttributedString to get indented textual content. When I’ve a protracted textual content and once I apply indents on paragraph fashion (NSParagraphStyle), My UITextView will get caught and I can not scroll to backside. I believe it has one thing to do with contentSize of the UITextView. I’ve tried all the pieces. It really works advantageous with textual content package 1, however I’ve to make use of textual content package 2 for its new options.
Here is my code:
UITextView
import UIKit
class MyTextView: UITextView {
comfort init(usingTextLayoutManager: Bool) {
self.init(body: .zero, textContainer: nil)
}
override init(body: CGRect, textContainer: NSTextContainer?) {
tremendous.init(body: body, textContainer: textContainer)
setUpTextView()
setUpAttributes()
setUpInputAccessoryView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been carried out")
}
func setUpTextView() {
alwaysBounceVertical = true
keyboardDismissMode = .interactiveWithAccessory
backgroundColor = .white
}
func setUpAttributes() {
let font = UIFont.systemFont(ofSize: 24, weight: .semibold)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = font.pointSize * 0.15
typingAttributes = [
.font: font,
.paragraphStyle: paragraphStyle
]
let string = "Once you strive your greatest, however you do not succeednWhen you get what you need, however not what you neednWhen you are feeling so drained, however you'll be able to't sleepnStuck in reversenAnd the tears come streaming down your facenWhen you lose one thing you'll be able to't replacenWhen you like somebody, nevertheless it goes to wastenCould or not it's worse?nLights will information you homenAnd ignite your bonesnAnd I'll attempt to repair younAnd excessive up above, or down belownWhen you are too in like to let it gonBut should you by no means strive, you may by no means knownJust what you are worthnLights will information you homenAnd ignite your bonesnAnd I'll attempt to repair younTears stream down your facenWhen you lose one thing you can not replacenTears stream down your face, and InTears stream down your facenI promise you I'll be taught from my mistakesnTears stream down your face, and InLights will information you homenAnd ignite your bonesnAnd I'll attempt to repair younWhen you strive your greatest, however you do not succeednWhen you get what you need, however not what you neednWhen you are feeling so drained, however you'll be able to't sleepnStuck in reversenAnd the tears come streaming down your facenWhen you lose one thing you'll be able to't replacenWhen you like somebody, nevertheless it goes to wastenCould or not it's worse?nLights will information you homenAnd ignite your bonesnAnd I'll attempt to repair younAnd excessive up above, or down belownWhen you are too in like to let it gonBut should you by no means strive, you may by no means knownJust what you are worthnLights will information you homenAnd ignite your bonesnAnd I'll attempt to repair younTears stream down your facenWhen you lose one thing you can not replacenTears stream down your face, and InTears stream down your facenI promise you I'll be taught from my mistakesnTears stream down your face, and InLights will information you homenAnd ignite your bonesnAnd I'll attempt to repair you"
let attributedString = NSMutableAttributedString(
string: string,
attributes: [
.font: font,
.paragraphStyle: paragraphStyle
]
)
textStorage.setAttributedString(attributedString)
}
func indentLeft() {
let mutableAttributedText = NSMutableAttributedString(attributedString: attributedText)
let paragraphRange = mutableAttributedText.mutableString.paragraphRange(for: selectedRange)
textStorage.enumerateAttribute(.paragraphStyle, in: paragraphRange) { attribute, vary, _ in
if let paragraphStyle = attribute as? NSParagraphStyle {
let newParagraphStyle = paragraphStyle.mutableCopy() as! NSMutableParagraphStyle
if newParagraphStyle.firstLineHeadIndent > 0 {
newParagraphStyle.firstLineHeadIndent -= 40
newParagraphStyle.headIndent -= 40
} else {
newParagraphStyle.firstLineHeadIndent = 0
newParagraphStyle.headIndent = 0
}
textStorage.addAttribute(.paragraphStyle, worth: newParagraphStyle, vary: vary)
}
}
}
func indentRight() {
let mutableAttributedText = NSMutableAttributedString(attributedString: attributedText)
let paragraphRange = mutableAttributedText.mutableString.paragraphRange(for: selectedRange)
textStorage.enumerateAttribute(.paragraphStyle, in: paragraphRange) { attribute, vary, _ in
if let paragraphStyle = attribute as? NSParagraphStyle {
let newParagraphStyle = paragraphStyle.mutableCopy() as! NSMutableParagraphStyle
if newParagraphStyle.firstLineHeadIndent < 160 {
newParagraphStyle.firstLineHeadIndent += 40
newParagraphStyle.headIndent += 40
} else {
newParagraphStyle.firstLineHeadIndent = 0
newParagraphStyle.headIndent = 0
}
textStorage.addAttribute(.paragraphStyle, worth: newParagraphStyle, vary: vary)
}
}
}
func setUpInputAccessoryView() {
let view = AccessoryView(body: .init(origin: .zero, dimension: .init(width: .zero, peak: 50)))
view.textView = self
inputAccessoryView = view
}
}
ViewController
class ViewController: UIViewController {
var textView: MyTextView!
override func viewDidLoad() {
tremendous.viewDidLoad()
view.backgroundColor = .white
setUpTextView()
}
func setUpTextView() {
textView = MyTextView()
textView.layer.borderColor = UIColor.black.cgColor
textView.layer.borderWidth = 2
view.addSubview(textView)
setUpConstraints()
let _ = textView.becomeFirstResponder()
}
func setUpConstraints() {
textView.translatesAutoresizingMaskIntoConstraints = false
[ textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
].forEach {
$0.isActive = true
}
}
}
Accent view for textView
class AccessoryView: UIView {
weak var textView: MyTextView?
let indentLeft: UIButton = {
let button = UIButton(kind: .system)
button.setImage(UIImage(systemName: "lessthan"), for: .regular)
return button
}()
let indentRight: UIButton = {
let button = UIButton(kind: .system)
button.setImage(UIImage(systemName: "greaterthan"), for: .regular)
return button
}()
override init(body: CGRect) {
tremendous.init(body: body)
backgroundColor = .white
layer.borderColor = UIColor.black.cgColor
layer.borderWidth = 2
addSubview(indentLeft)
indentLeft.translatesAutoresizingMaskIntoConstraints = false
indentLeft.topAnchor.constraint(equalTo: topAnchor).isActive = true
indentLeft.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
indentLeft.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
indentLeft.widthAnchor.constraint(equalToConstant: 55).isActive = true
indentLeft.addTarget(self, motion: #selector(handleIndentLeft), for: .touchUpInside)
addSubview(indentRight)
indentRight.translatesAutoresizingMaskIntoConstraints = false
indentRight.topAnchor.constraint(equalTo: topAnchor).isActive = true
indentRight.leadingAnchor.constraint(equalTo: indentLeft.trailingAnchor).isActive = true
indentRight.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
indentRight.widthAnchor.constraint(equalToConstant: 55).isActive = true
indentRight.addTarget(self, motion: #selector(handleIndentRight), for: .touchUpInside)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been carried out")
}
@objc func handleIndentLeft(button: UIButton) {
guard let textView = textView else {
return
}
textView.indentLeft()
}
@objc func handleIndentRight(button: UIButton) {
guard let textView = textView else {
return
}
textView.indentRight()
}
}
How recreate subject:
- Run it in your telephone.
- Choose all textual content.
- press proper indent button 2-3 occasions
- then scroll to prime and once more scroll to backside.
Have you learnt why is it taking place and the way can I resolve it?