Reputation: 3961
I have a View Controller that contains a UITextView. This UITextView gets the text updated programmatically depending on the data model controlling it (around 30 different text options). The content in the UITextView will always be quite long, at about 450 words each. Paragraphs will range from 2 - 5.
It all works perfect. I just need a visual way to break up the paragraphs. Right now, I'm using \n\n to create a couple of line breaks in between paragraphs. While this works, I ultimately need headings.
In IB, I changed the text property from Plain to Attributed. This allows me to modify the text in a WYSIWYG-like fashion right in Xcode. Works great, but I am not using IB to change the text.
I did try using a WebView (code below). I created an extremely basic HTML file with H1 and P tags. This will unfortunately create 30+ HTML files in my project. I don't like this from a manageability standpoint. The text also can NOT be selectable, which it is with a webview (I don't want to resort to CSS [-webkit-user-select] to mitigate this issue.)
let path: String? = Bundle.main.path(forResource: "myHTML", ofType: "html")
let file = try? String(contentsOfFile: path!, encoding: String.Encoding.utf8)
let baseURL = URL(fileURLWithPath: Bundle.main.bundlePath)
self.myWebView.loadHTMLString(file!, baseURL: baseURL)
My ideal solution
Can I stick with my current setup? Programmatically changing the UITextView text with my data model, WHILE being able to change the text attributes in code to create my paragraph headings?
Edit - Trying to get this to work with MVC
allFormattedDescriptions: [
Formatted(heading: "heading 1", descriptionText: "Lorem Ipsum Paragraph 1"),
Formatted(heading: "heading 2", descriptionText: "Lorem Ipsum Paragraph 2"),
Formatted(heading: "heading 3", descriptionText: "Lorem Ipsum Paragraph 3")
]
// Ideal formatting; every paragraph will have a heading. Can handle that with one object that requires both a heading and description text (paragraph).
struct Formatted {
var heading: String!
var descriptionText: String!
var bodyParagraphStyle: NSMutableParagraphStyle = {
let style = NSMutableParagraphStyle()
style.lineSpacing = 10
style.paragraphSpacingBefore = 6
style.paragraphSpacing = 6
return style
}()
var headerParagraphStyle: NSMutableParagraphStyle = {
let style = NSMutableParagraphStyle()
style.paragraphSpacingBefore = 24
return style
}()
var bodyAttributes: [String: AnyObject]!
var headerAttributes: [String: AnyObject]!
}
Upvotes: 3
Views: 3073
Reputation: 93151
Use NS(Mutable)ParagraphStyle
and NS(Mutable)AttributedString
. For example:
class ViewController: UIViewController {
@IBOutlet weak var textView: UITextView!
var allFormattedDescriptions = [
Formatted(heading: "Introduction to Bacon Ipsum", descriptionText: "Bacon ipsum dolor amet jerky pig pastrami capicola biltong turkey, ball tip fatback andouille porchetta flank swine brisket bacon pork loin. Tongue shank cupim, pastrami spare ribs meatball drumstick pork pork chop. Sirloin flank tenderloin bresaola doner, cupim ribeye drumstick ham hock t-bone pork short ribs shoulder. Fatback ribeye pastrami pancetta, chuck turkey andouille boudin burgdoggen shoulder tongue kielbasa doner shankle turducken. Rump strip steak drumstick, shankle cupim prosciutto jerky bacon doner. Pork chop jowl burgdoggen, cow turkey ball tip doner. Cow ham meatball chuck flank meatloaf prosciutto."),
Formatted(heading: "Kielbasa?", descriptionText: "Spare ribs boudin ham leberkas landjaeger filet mignon. Short loin fatback hamburger leberkas chicken. Frankfurter chuck short ribs ball tip, ground round cupim shank brisket venison turducken boudin. Pig sirloin pork loin meatloaf short loin turkey swine.")
]
override func viewDidLoad() {
super.viewDidLoad()
let textContent = NSMutableAttributedString()
for (index, desc) in allFormattedDescriptions.enumerated() {
let includeLinebreak = index < allFormattedDescriptions.count - 1
textContent.append(desc.attributeString(includeLineBreak: includeLinebreak))
}
textView.attributedText = textContent
}
}
struct Formatted {
var heading: String
var descriptionText: String
var bodyParagraphStyle: NSMutableParagraphStyle = {
let style = NSMutableParagraphStyle()
style.lineSpacing = 10
style.paragraphSpacingBefore = 6
style.paragraphSpacing = 6
return style
}()
var headerParagraphStyle: NSMutableParagraphStyle = {
let style = NSMutableParagraphStyle()
style.paragraphSpacingBefore = 24
return style
}()
var bodyAttributes: [NSAttributedStringKey: Any]
var headerAttributes: [NSAttributedStringKey: Any]
func attributeString(includeLineBreak: Bool = true) -> NSAttributedString {
let result = NSMutableAttributedString()
result.append(NSAttributedString(string: self.heading + "\n", attributes: self.headerAttributes))
result.append(NSAttributedString(string: self.descriptionText, attributes: self.bodyAttributes))
if includeLineBreak {
result.append(NSAttributedString(string: "\n", attributes: self.bodyAttributes))
}
return result as NSAttributedString
}
init(heading: String, descriptionText: String) {
self.heading = heading
self.descriptionText = descriptionText
self.bodyAttributes = [
NSAttributedStringKey.font: UIFont(name: "Hoefler Text", size: 14)!,
NSAttributedStringKey.paragraphStyle: bodyParagraphStyle
]
self.headerAttributes = [
NSAttributedStringKey.font: UIFont(name: "Avenir", size: 22)!,
NSAttributedStringKey.paragraphStyle: headerParagraphStyle,
NSAttributedStringKey.foregroundColor: UIColor.red
]
}
}
Result:
Upvotes: 8