accessoryVIew can now center itself in relation to the hero character.

This commit is contained in:
Kevin G Christiano 2019-09-18 14:13:33 -04:00
parent 52387fe9c9
commit 5bc420ac05
5 changed files with 61 additions and 80 deletions

View File

@ -25,6 +25,8 @@ public typealias ActionBlock = () -> ()
/// Set this to use a custom sizing object during updateView instead of the standard. /// Set this to use a custom sizing object during updateView instead of the standard.
public var sizeObject: MFSizeObject? public var sizeObject: MFSizeObject?
public var scaleSize: NSNumber? public var scaleSize: NSNumber?
/// A specific text index to use as a unique marker.
public var hero: Int? public var hero: Int?
// Used for scaling the font in updateView. // Used for scaling the font in updateView.
@ -503,18 +505,7 @@ public typealias ActionBlock = () -> ()
let accessibleAction = customAccessibilityAction(range: range) let accessibleAction = customAccessibilityAction(range: range)
clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1)) clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1))
} }
// func rectOfCharacter() {
//
//// label.intrinsicContentSize.width
// if let range = mylabel.text?.range(of: String(describing: mylabel.text?.characters.last!)) {
// let prefix = mylabel.text?.substring(to: range.lowerBound)
// let size: CGSize = prefix!.size(attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 35.0)])
// let position = CGPoint(x:size.width,y: 0)
// myScrollView.setContentOffset(position, animated: true)
// }
// }
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
guard let attributedText = label.attributedText else { return CGRect() } guard let attributedText = label.attributedText else { return CGRect() }
@ -567,15 +558,6 @@ extension Label {
hero = json?["hero"] as? Int hero = json?["hero"] as? Int
} }
func noticeBounds(_ rect: CGRect, color: UIColor) -> UIView {
let view = UIView(frame: rect)
view.layer.borderColor = color.cgColor
view.layer.borderWidth = 1.0
return view
}
public func setAsMolecule() { public func setAsMolecule() {
styleB2(true) styleB2(true)
} }

View File

@ -166,10 +166,5 @@ import UIKit
} else { } else {
button.isHidden = true button.isHidden = true
} }
if let text = header.messageLabel.text, text.contains("frog") {
let rect = Label.boundingRect(forCharacterRange: NSRange(location: 65, length: 4), in: header.messageLabel)
print(rect)
}
} }
} }

View File

@ -53,6 +53,16 @@ import UIKit
} }
} }
open override func layoutSubviews() {
super.layoutSubviews()
if let center = heroAccessoryCenter {
accessoryView?.center.y = center.y
}
}
var heroAccessoryCenter: CGPoint?
func styleStandard() { func styleStandard() {
topMarginPadding = 24 topMarginPadding = 24
bottomMarginPadding = 24 bottomMarginPadding = 24
@ -128,15 +138,51 @@ import UIKit
} }
} }
/// NOTE: Should only be called when displayed or about to be displayed.
public func alignAccessoryToHero() {
layoutIfNeeded()
guard let heroLabel = findHeroLabel(views: contentView.subviews), let hero = heroLabel.hero else { return }
let rect = Label.boundingRect(forCharacterRange: NSRange(location: hero, length: 1), in: heroLabel)
let rectView = UIView(frame: rect)
heroLabel.addSubview(rectView)
accessoryView?.center.y = contentView.convert(rectView.center, from: heroLabel).y
heroAccessoryCenter = accessoryView?.center
rectView.removeFromSuperview()
}
/**
Used to traverse the view hierarchy for a 🦸heroic Label.
*/
private func findHeroLabel(views: [UIView]) -> Label? {
if views.isEmpty {
return nil
}
var queue = [UIView]()
for view in views {
// Only one Label will have a hero in a table cell.
if let label = view as? Label, label.hero != nil {
return label
}
queue.append(contentsOf: view.subviews)
}
return findHeroLabel(views: queue)
}
// MARK: - MVMCoreUIMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.json = json; self.json = json
style(with: json?.optionalStringForKey("style")) style(with: json?.optionalStringForKey("style"))
if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") { if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") {
updateViewHorizontalDefaults = useHorizontalMargins updateViewHorizontalDefaults = useHorizontalMargins
} }
if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false { if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false {
topMarginPadding = 0 topMarginPadding = 0
bottomMarginPadding = 0 bottomMarginPadding = 0
@ -161,9 +207,8 @@ import UIKit
bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData) bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData)
} }
guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return }
return
}
if molecule == nil { if molecule == nil {
if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) {
contentView.addSubview(moleculeView) contentView.addSubview(moleculeView)
@ -215,9 +260,7 @@ import UIKit
// MARK: - Arrow // MARK: - Arrow
/// Adds the standard mvm style caret to the accessory view /// Adds the standard mvm style caret to the accessory view
public func addCaretViewAccessory() { public func addCaretViewAccessory() {
guard accessoryView == nil else { guard accessoryView == nil else { return }
return
}
let width: CGFloat = 6 let width: CGFloat = 6
let height: CGFloat = 10 let height: CGFloat = 10
caretView = CaretView(lineThickness: CaretView.thin) caretView = CaretView(lineThickness: CaretView.thin)

View File

@ -24,6 +24,8 @@
/// Resets to default state before set with json is called again. /// Resets to default state before set with json is called again.
- (void)reset; - (void)reset;
/// Currently designed for UITableViewCell. Aligns the accessory view with the center of a character in a line of text.
- (void)alignAccessoryToHero;
/// For the molecule list to load more efficiently. /// For the molecule list to load more efficiently.
+ (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; + (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject;

View File

@ -51,9 +51,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
// MARK: - table // MARK: - table
open override func registerWithTable() { open override func registerWithTable() {
super.registerWithTable() super.registerWithTable()
guard let moleculesInfo = moleculesInfo else { guard let moleculesInfo = moleculesInfo else { return }
return
}
for moleculeInfo in moleculesInfo { for moleculeInfo in moleculesInfo {
tableView?.register(moleculeInfo.class, forCellReuseIdentifier: moleculeInfo.identifier) tableView?.register(moleculeInfo.class, forCellReuseIdentifier: moleculeInfo.identifier)
} }
@ -90,34 +89,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.layoutIfNeeded() if cell.accessoryView != nil, let protocolCell = cell as? (MoleculeTableViewCell & MVMCoreUIMoleculeViewProtocol) {
let heroLabel = findHeroLabel(views: cell.contentView.subviews) protocolCell.alignAccessoryToHero()
let rect = Label.boundingRect(forCharacterRange: NSRange(location: heroLabel!.hero!, length: 1), in: heroLabel!) }
let rectView = heroLabel?.noticeBounds(rect, color: .clear)
heroLabel?.addSubview(rectView!)
let convert = cell.contentView.convert(rectView!.center, from: heroLabel)
cell.accessoryView?.center.y = convert.y
rectView?.removeFromSuperview()
}
// TODO: Write recursively.
func findHeroLabel(views: [UIView]) -> Label? {
var queue: [UIView] = views
repeat {
for view in queue {
if let label = view as? Label, label.hero != nil {
return label
} else if !view.subviews.isEmpty {
queue.append(contentsOf: view.subviews)
}
queue.removeFirst()
}
} while (!queue.isEmpty)
return nil
} }
open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -143,31 +117,16 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
if let tableView = tableView { if let tableView = tableView {
let point = molecule.convert(molecule.bounds.origin, to: tableView) let point = molecule.convert(molecule.bounds.origin, to: tableView)
if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false {
//-------------
tableView.beginUpdates() tableView.beginUpdates()
// let cell = tableView.cellForRow(at: indexPath)
// cell.layoutIfNeeded()
// let heroLabel = findHeroLabel(views: cell.contentView.subviews)
// let rect = Label.boundingRect(forCharacterRange: NSRange(location: heroLabel!.hero!, length: 1), in: heroLabel!)
// let rectView = heroLabel?.noticeBounds(rect, color: .blue)
// heroLabel?.addSubview(rectView!)
// // let rect = Label.boundingRect(forCharacterRange: NSRange(location: 46, length: 6), in: self)
// let convert = cell.contentView.convert(rectView!.center, from: heroLabel)
//
// cell.accessoryView?.center.y = convert.y
/// ------------
tableView.endUpdates() tableView.endUpdates()
} }
} }
} }
open override func addMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) { open override func addMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {
// This dispatch is needed to fix a race condition that can occur if this function is called during the table setup. // This dispatch is needed to fix a race condition that can occur if this function is called during the table setup.
DispatchQueue.main.async { DispatchQueue.main.async {
guard let cell = sender as? MoleculeTableViewCell, let indexPath = self.tableView?.indexPath(for: cell) else { guard let cell = sender as? MoleculeTableViewCell, let indexPath = self.tableView?.indexPath(for: cell) else { return }
return
}
var indexPaths: [IndexPath] = [] var indexPaths: [IndexPath] = []
for molecule in molecules { for molecule in molecules {
if let info = self.getMoleculeInfo(with: molecule) { if let info = self.getMoleculeInfo(with: molecule) {