Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement blockquotes with paragraph style #168

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Aztec/Classes/Formatters/AttributeFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ extension CharacterAttributeFormatter {
func toggleAttribute(inTextView textView: UITextView, atRange range: NSRange) {
let applicationRange = self.applicationRange(forRange: range, inString: textView.textStorage)

guard applicationRange.length > 0 else {
toggleTypingAttribute(inTextView: textView)
guard applicationRange.length > 0 || textView.textStorage.length > 0 else {
return
}
toggleAttribute(inString: textView.textStorage, atRange: applicationRange)
Expand All @@ -144,11 +143,15 @@ extension ParagraphAttributeFormatter {
func toggleAttribute(inTextView textView: UITextView, atRange range: NSRange) {
let applicationRange = self.applicationRange(forRange: range, inString: textView.textStorage)

guard applicationRange.length > 0 else {
toggleTypingAttribute(inTextView: textView)
if applicationRange.length == 0 || textView.textStorage.length == 0 {
insertEmptyAttribute(inString: textView.textStorage, at: applicationRange.location)
return
textView.selectedRange = NSRange(location: applicationRange.location, length: 1)
}
toggleAttribute(inString: textView.textStorage, atRange: applicationRange)
}

func applyAttribute(inTextView textView: UITextView, atRange range: NSRange) {
let applicationRange = self.applicationRange(forRange: range, inString: textView.textStorage)
applyAttributes(toString: textView.textStorage, atRange: applicationRange)
}
}
17 changes: 11 additions & 6 deletions Aztec/Classes/Formatters/BlockquoteFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import Foundation
import UIKit

class Blockquote: NSObject, NSCoding {
static let attributeName = "AZBlockquote"

public func encode(with aCoder: NSCoder) {

}
Expand All @@ -15,25 +13,32 @@ class Blockquote: NSObject, NSCoding {
required public init?(coder aDecoder: NSCoder){

}

static func ==(lhs: Blockquote, rhs: Blockquote) -> Bool {
return true
}
}

struct BlockquoteFormatter: ParagraphAttributeFormatter {
let attributes: [String: AnyObject] = {
let style = NSMutableParagraphStyle()
let style = ParagraphStyle()
style.headIndent = Metrics.defaultIndentation
style.firstLineHeadIndent = style.headIndent
style.tailIndent = -Metrics.defaultIndentation
style.paragraphSpacing = Metrics.defaultIndentation
style.paragraphSpacingBefore = Metrics.defaultIndentation
style.blockquote = Blockquote()

return [
NSParagraphStyleAttributeName: style,
Blockquote.attributeName: Blockquote()
NSParagraphStyleAttributeName: style
]
}()

func present(inAttributes attributes: [String : AnyObject]) -> Bool {
return attributes[Blockquote.attributeName] is Blockquote
if let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle {
return paragraphStyle.blockquote != nil
}
return false
}
}

10 changes: 7 additions & 3 deletions Aztec/Classes/TextKit/LayoutManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ class LayoutManager: NSLayoutManager {
}

let characterRange = self.characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil)
textStorage.enumerateAttribute(Blockquote.attributeName, in: characterRange, options: []){ (object, range, stop) in
guard object is Blockquote else {
return

//draw blockquotes
textStorage.enumerateAttribute(NSParagraphStyleAttributeName, in: characterRange, options: []){ (object, range, stop) in
guard let paragraphStyle = object as? ParagraphStyle,
let _ = paragraphStyle.blockquote
else {
return
}

let borderColor = UIColor(red: 0.5294117647, green: 0.650980392156863, blue: 0.737254902, alpha: 1.0)
Expand Down
28 changes: 16 additions & 12 deletions Aztec/Classes/TextKit/ParagraphStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ import UIKit
open class ParagraphStyle: NSMutableParagraphStyle {

var textList: TextList?
var blockquote: Blockquote?

override init() {
textList = nil
blockquote = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optionals are nil by default. You don't really need these two lines

super.init()
}

public required init?(coder aDecoder: NSCoder) {
textList = nil
blockquote = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above

if aDecoder.containsValue(forKey: String(describing: TextList.self)) {
let styleRaw = aDecoder.decodeInteger(forKey: String(describing: TextList.self))
if let style = TextList.Style(rawValue:styleRaw) {
textList = TextList(style: style)
}
}
if aDecoder.containsValue(forKey: String(describing:Blockquote.self)) {
blockquote = aDecoder.decodeObject(forKey: String(describing:Blockquote.self)) as? Blockquote
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space between describing: and the class kind

super.init(coder: aDecoder)
}

Expand All @@ -26,11 +32,16 @@ open class ParagraphStyle: NSMutableParagraphStyle {
if let textListSet = textList {
aCoder.encode(textListSet.style.rawValue, forKey: String(describing: TextList.self))
}

if let blockquote = self.blockquote {
aCoder.encode(blockquote, forKey: String(describing:Blockquote.self))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

}
}

override open func setParagraphStyle(_ obj: NSParagraphStyle) {
if let paragrahStyle = obj as? ParagraphStyle {
textList = paragrahStyle.textList
blockquote = paragrahStyle.blockquote
}
super.setParagraphStyle(obj)
}
Expand Down Expand Up @@ -69,21 +80,14 @@ open class ParagraphStyle: NSMutableParagraphStyle {
return false
}

if textList == nil || otherParagraph.textList == nil {
return super.isEqual(object)
}

if textList == nil && otherParagraph.textList != nil {
if textList != otherParagraph.textList {
return false
}

if textList != nil && otherParagraph.textList == nil {
if blockquote != otherParagraph.blockquote {
return false
}

if textList! != otherParagraph.textList! {
return false
}

return super.isEqual(object)
}
Expand All @@ -97,7 +101,7 @@ open class ParagraphStyle: NSMutableParagraphStyle {
let thisResult = ParagraphStyle()
thisResult.setParagraphStyle(result as! NSParagraphStyle)
thisResult.textList = textList

thisResult.blockquote = blockquote
return thisResult
}

Expand All @@ -106,7 +110,7 @@ open class ParagraphStyle: NSMutableParagraphStyle {
let thisResult = ParagraphStyle()
thisResult.setParagraphStyle(result as! NSParagraphStyle)
thisResult.textList = textList

thisResult.blockquote = blockquote
return thisResult
}

Expand All @@ -115,6 +119,6 @@ open class ParagraphStyle: NSMutableParagraphStyle {
}

open override var description:String {
return super.description + "\nTextList:\(textList?.style)"
return super.description + "\nTextList:\(textList?.style)\nBlockquote:\(blockquote)"
}
}
82 changes: 73 additions & 9 deletions Aztec/Classes/TextKit/TextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ open class TextView: UITextView {
super.insertText(text)
insertionRange.length = 1
refreshListAfterInsertionOf(text: text, range: insertionRange)
refreshBlockquoteAfterInsertionOf(text: text, range: insertionRange)
}

open override func deleteBackward() {
Expand All @@ -126,15 +127,9 @@ open class TextView: UITextView {
if storage.string.isEmpty {
return
}
if deletedString.string == "\n" || deletionRange.location == 0 {
var isPreviousLocationList = false
if (selectedRange.location > 0) {
isPreviousLocationList = storage.textListAttribute(atIndex: selectedRange.location - 1) != nil
}
if !isPreviousLocationList {
removeList(aroundRange: selectedRange)
}
}

refreshListAfterDeletionOf(text: deletedString, atRange: deletionRange)
refreshBlockquoteAfterDeletionOf(text: deletedString, atRange: deletionRange)
}

// MARK: - UIView Overrides
Expand Down Expand Up @@ -454,6 +449,22 @@ open class TextView: UITextView {
}
}

/// Refresh Lists attributes when text is deleted in the specified range
///
/// - Parameters:
/// - text: the text being added
/// - range: the range of the insertion of the new text
private func refreshListAfterDeletionOf(text deletedText: NSAttributedString, atRange range:NSRange) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing // between the last doc's line and the method signature

guard deletedText.textListAttribute(atIndex: 0) != nil,
deletedText.string == "\n" || range.location == 0 else {
return
}

if (range.location == 0) {
removeList(aroundRange: range)
}
}

fileprivate func removeList(aroundRange range: NSRange) {
let formatter = TextListFormatter()
formatter.removeList(inString: storage, atRange: range)
Expand Down Expand Up @@ -495,6 +506,59 @@ open class TextView: UITextView {
formatter.toggleAttribute(inTextView: self, atRange: range)
}

/// Refresh Lists attributes when text is deleted in the specified range
///
/// - Parameters:
/// - text: the text being added
/// - range: the range of the insertion of the new text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

private func refreshBlockquoteAfterDeletionOf(text deletedText: NSAttributedString, atRange range:NSRange) {
let formatter = BlockquoteFormatter()
guard formatter.attribute(inTextView: self, at: range.location),
deletedText.string == "\n" || range.location == 0 else {
return
}

if (range.location == 0) {
formatter.toggleAttribute(inTextView: self, atRange: range)
}
}

/// Refresh blockquotes attributes when inserting new text in the specified range
///
/// - Parameters:
/// - text: the text being added
/// - range: the range of the insertion of the new text
private func refreshBlockquoteAfterInsertionOf(text:String, range:NSRange) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaand same as above!

let formatter = BlockquoteFormatter()
guard formatter.attribute(inTextView: self, at: range.location) else {
return
}

let afterRange = NSRange(location: range.location + 1, length: 1)
let beforeRange = NSRange(location: range.location - 1, length: 1)

var afterString = "\n"
var beforeString = "\n"
if beforeRange.location >= 0 {
beforeString = storage.attributedSubstring(from: beforeRange).string
}
if afterRange.endLocation < storage.length {
afterString = storage.attributedSubstring(from: afterRange).string
}

let isBegginingOfListItem = storage.isStartOfNewLine(atLocation: range.location)

if text == "\n" && beforeString == "\n" && afterString == "\n" && isBegginingOfListItem {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this could be refactored (anyhow) using CharacterSet.newlines?

formatter.toggleAttribute(inTextView: self, atRange: range)
if afterRange.endLocation < storage.length {
formatter.toggleAttribute(inTextView: self, atRange: afterRange)
deleteBackward()
} else {
selectedRange = NSRange(location: range.location, length: 0)
}
}
}


// MARK: - Links

Expand Down