Merge branch 'develop' into feature/swiftified_textField

This commit is contained in:
Kevin G Christiano 2019-10-21 12:12:19 -04:00
commit 4742eace6b
19 changed files with 257 additions and 215 deletions

View File

@ -83,11 +83,9 @@
self.enabled = YES; self.enabled = YES;
// Disable SmartQuotes // Disable SmartQuotes
if (@available(iOS 11.0, *)) { self.textField.smartQuotesType = UITextSmartQuotesTypeNo;
self.textField.smartQuotesType = UITextSmartQuotesTypeNo; self.textField.smartDashesType = UITextSmartDashesTypeNo;
self.textField.smartDashesType = UITextSmartDashesTypeNo; self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
}
} }
} }

View File

@ -8,22 +8,6 @@
import MVMCore import MVMCore
/*
!!! -- DO NOT REMOVE -- !!!
(Unless Design changes the appearance of the checkmark).
// Offsets based on the 124x124 example checkmark
let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871
let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225
let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774
let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516
let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935
let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097
let pivotPercentage: Float = 0.34
let endPercentage = 1.0 - pivotPercentage
let animationInterval: Float = 0.01
*/
/** /**
This class expects its height and width to be equal. This class expects its height and width to be equal.
*/ */
@ -49,7 +33,7 @@ import MVMCore
public var updateSelectionOnly: Bool = false public var updateSelectionOnly: Bool = false
/// The color of the background when checked. /// The color of the background when checked.
public var checkedBackgroundColor: UIColor = .white { public var checkedBackgroundColor: UIColor = .clear {
didSet { didSet {
if isSelected { if isSelected {
backgroundColor = checkedBackgroundColor backgroundColor = checkedBackgroundColor
@ -58,7 +42,7 @@ import MVMCore
} }
/// The color of the background when unChecked. /// The color of the background when unChecked.
public var unCheckedBackgroundColor: UIColor = .white { public var unCheckedBackgroundColor: UIColor = .clear {
didSet { didSet {
if !isSelected { if !isSelected {
backgroundColor = unCheckedBackgroundColor backgroundColor = unCheckedBackgroundColor
@ -91,11 +75,7 @@ import MVMCore
/// Color of the check mark. /// Color of the check mark.
public var checkColor: UIColor = .black { public var checkColor: UIColor = .black {
didSet { didSet {
if let shapeLayer = shapeLayer { setShapeLayerStrokeColor(checkColor)
CATransaction.withDisabledAnimations {
shapeLayer.strokeColor = checkColor.cgColor
}
}
} }
} }
@ -135,6 +115,21 @@ import MVMCore
} }
} }
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
private var heightConstraint: NSLayoutConstraint?
private var widthConstraint: NSLayoutConstraint?
/// Updates the height and width anchors of the Checkbox with the assigned value.
public var heigthWidthConstant: CGFloat = Checkbox.defaultHeightWidth {
didSet {
heightConstraint?.constant = heigthWidthConstant
widthConstraint?.constant = heigthWidthConstant
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -145,7 +140,6 @@ import MVMCore
accessibilityTraits = .button accessibilityTraits = .button
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint")
updateAccessibilityLabel() updateAccessibilityLabel()
setupView() setupView()
} }
@ -182,7 +176,7 @@ import MVMCore
override open func layoutSubviews() { override open func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
drawCheck() drawShapeLayer()
layer.cornerRadius = isRound ? cornerRadiusValue : 0 layer.cornerRadius = isRound ? cornerRadiusValue : 0
layer.borderWidth = borderWidth layer.borderWidth = borderWidth
layer.borderColor = borderColor.cgColor layer.borderColor = borderColor.cgColor
@ -190,9 +184,15 @@ import MVMCore
open func setupView() { open func setupView() {
guard constraints.isEmpty else { return }
isUserInteractionEnabled = true isUserInteractionEnabled = true
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .white backgroundColor = .clear
widthConstraint = widthAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth)
heightConstraint = heightAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth)
heightWidthIsActive(true)
} }
//-------------------------------------------------- //--------------------------------------------------
@ -220,8 +220,8 @@ import MVMCore
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
/// Creates the check mark used for the checkbox. /// Creates the check mark layer.
private func drawCheck() { private func drawShapeLayer() {
if shapeLayer == nil { if shapeLayer == nil {
@ -241,13 +241,18 @@ import MVMCore
} }
} }
/// Returns a UIBezierPath detailing the path of a checkmark /// - returns: The CGPath of a UIBezierPath detailing the path of a checkmark
func checkMarkPath() -> CGPath { func checkMarkPath() -> CGPath {
let sideLength = max(bounds.size.height, bounds.size.width) let length = max(bounds.size.height, bounds.size.width)
let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength) let xInsetLeft = length * 0.25
let pivotOffSet = CGPoint(x: 0.46774 * sideLength, y: 0.64516 * sideLength) let yInsetTop = length * 0.3
let endOffset = CGPoint(x: 0.66935 * sideLength , y: 0.37097 * sideLength) let innerWidth = length - (xInsetLeft + length * 0.25) // + Right X Inset
let innerHeight = length - (yInsetTop + length * 0.35) // + Bottom Y Inset
let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2))
let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight)
let endOffset = CGPoint(x: xInsetLeft + innerWidth, y: yInsetTop)
let bezierPath = UIBezierPath() let bezierPath = UIBezierPath()
bezierPath.move(to: startPoint) bezierPath.move(to: startPoint)
@ -267,14 +272,14 @@ import MVMCore
self.updateSelectionOnly = true self.updateSelectionOnly = true
self.isSelected = selected self.isSelected = selected
self.updateSelectionOnly = false self.updateSelectionOnly = false
self.drawCheck() self.drawShapeLayer()
self.shapeLayer?.removeAllAnimations() self.shapeLayer?.removeAllAnimations()
self.updateCheckboxUI(isSelected: selected, isAnimated: animated) self.updateCheckboxUI(isSelected: selected, isAnimated: animated)
} }
} }
/// updates the visuals of the check mark and background. /// updates the visuals of the check mark and background.
/// - parameter isSelection: the check state of the checkbox. /// - parameter isSelected: the check state of the checkbox.
/// - parameter isAnimated: determines of the changes should animate or immediately refelect. /// - parameter isAnimated: determines of the changes should animate or immediately refelect.
public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) { public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) {
@ -296,7 +301,7 @@ import MVMCore
self.shapeLayer?.strokeEnd = isSelected ? 1 : 0 self.shapeLayer?.strokeEnd = isSelected ? 1 : 0
} }
self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor
} }
} }
@ -308,6 +313,38 @@ import MVMCore
} }
} }
func isEnabled(_ enabled: Bool) {
isUserInteractionEnabled = enabled
if enabled {
layer.borderColor = borderColor.cgColor
backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor
alpha = 1.0
setShapeLayerStrokeColor(checkColor)
} else {
layer.borderColor = UIColor.mfSilver().cgColor
backgroundColor = .clear
alpha = DisableOppacity
setShapeLayerStrokeColor(UIColor.mfSilver())
}
}
private func setShapeLayerStrokeColor(_ color: UIColor) {
if let shapeLayer = shapeLayer {
CATransaction.withDisabledAnimations {
shapeLayer.strokeColor = color.cgColor
}
}
}
public func heightWidthIsActive(_ isActive: Bool) {
heightConstraint?.isActive = isActive
widthConstraint?.isActive = isActive
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - UITouch // MARK: - UITouch
//-------------------------------------------------- //--------------------------------------------------
@ -343,10 +380,11 @@ import MVMCore
open func reset() { open func reset() {
isEnabled(true)
shapeLayer?.removeAllAnimations() shapeLayer?.removeAllAnimations()
shapeLayer?.removeFromSuperlayer() shapeLayer?.removeFromSuperlayer()
shapeLayer = nil shapeLayer = nil
backgroundColor = nil backgroundColor = .clear
borderColor = .black borderColor = .black
borderWidth = 1.0 borderWidth = 1.0
checkColor = .black checkColor = .black
@ -413,6 +451,10 @@ import MVMCore
self.isRound = isRound self.isRound = isRound
} }
if let enabled = dictionary["isEnabled"] as? Bool {
isEnabled(enabled)
}
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
} }

View File

@ -26,6 +26,9 @@ public typealias ActionBlock = () -> ()
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?
// Used for scaling the font in updateView. // Used for scaling the font in updateView.
private var originalAttributedString: NSAttributedString? private var originalAttributedString: NSAttributedString?
@ -502,6 +505,50 @@ 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))
} }
/**
Provides a text container and layout manager of how the text would appear on screen.
They are used in tandem to derive low-level TextKit results of the label.
*/
public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? {
// Must configure the attributed string to translate what would appear on screen to accurately analyze.
guard let attributedText = attributedText else { return nil }
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = textAlignment
let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText)
stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count))
let textStorage = NSTextStorage(attributedString: stagedAttributedString)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
textContainer.size = bounds.size
return (textContainer, layoutManager, textStorage)
}
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
guard let abstractContainer = label.abstractTextContainer() else { return CGRect() }
let textContainer = abstractContainer.0
let layoutManager = abstractContainer.1
var glyphRange = NSRange()
// Convert the range for glyphs.
layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange)
return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
}
} }
// MARK: - Atomization // MARK: - Atomization
@ -521,6 +568,8 @@ extension Label {
clauses = [] clauses = []
Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData) Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData)
originalAttributedString = attributedText originalAttributedString = attributedText
hero = json?["hero"] as? Int
} }
public func setAsMolecule() { public func setAsMolecule() {
@ -634,26 +683,9 @@ extension UITapGestureRecognizer {
return true return true
} }
// Must configure the attributed string to translate what would appear on screen to accurately analyze. guard let abstractContainer = label.abstractTextContainer() else { return false }
guard let attributedText = label.attributedText else { return false } let textContainer = abstractContainer.0
let layoutManager = abstractContainer.1
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = label.textAlignment
let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText)
stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count))
let textStorage = NSTextStorage(attributedString: stagedAttributedString)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
textContainer.size = label.bounds.size
let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer) let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer)

View File

@ -173,11 +173,10 @@
view.placeHolderLabel.textColor = [UIColor mfLightGrayColor]; view.placeHolderLabel.textColor = [UIColor mfLightGrayColor];
// Disable SmartQuotes // Disable SmartQuotes
if (@available(iOS 11.0, *)) { view.textView.smartQuotesType = UITextSmartQuotesTypeNo;
view.textView.smartQuotesType = UITextSmartQuotesTypeNo; view.textView.smartDashesType = UITextSmartDashesTypeNo;
view.textView.smartDashesType = UITextSmartDashesTypeNo; view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
}
[view didSetFont:view.textView.font]; [view didSetFont:view.textView.font];
view.hideBorder = YES; view.hideBorder = YES;
return view; return view;

View File

@ -48,9 +48,7 @@
- (void)setAsMolecule { - (void)setAsMolecule {
self.translatesAutoresizingMaskIntoConstraints = NO; self.translatesAutoresizingMaskIntoConstraints = NO;
if (@available(iOS 11.0, *)) { self.insetsLayoutMarginsFromSafeArea = NO;
self.insetsLayoutMarginsFromSafeArea = NO;
}
} }
- (void)reset { - (void)reset {

View File

@ -61,12 +61,8 @@
tableView.separatorStyle = UITableViewCellSeparatorStyleNone; tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.delegate = self; tableView.delegate = self;
tableView.dataSource = self; tableView.dataSource = self;
if (@available(iOS 11.0, *)) { tableView.insetsContentViewsToSafeArea = NO;
tableView.insetsContentViewsToSafeArea = NO; tableView.cellLayoutMarginsFollowReadableWidth = NO;
}
if ([tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) {
tableView.cellLayoutMarginsFollowReadableWidth = NO;
}
return tableView; return tableView;
} }

View File

@ -91,10 +91,7 @@ static NSTimeInterval const HandScrollAnimationTiming = 7.f;
- (void)viewDidLayoutSubviews { - (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews]; [super viewDidLayoutSubviews];
BOOL automaticInset = NO; BOOL automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic;
if (@available(iOS 11.0, *)) {
automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic;
}
// Takes into account the navigation bar. // Takes into account the navigation bar.
if (!automaticInset && (self.edgesForExtendedLayout & UIRectEdgeTop)) { if (!automaticInset && (self.edgesForExtendedLayout & UIRectEdgeTop)) {

View File

@ -190,13 +190,9 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true
footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true
if #available(iOS 11.0, *) { view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view) safeAreaView?.backgroundColor = bottomView?.backgroundColor
safeAreaView?.backgroundColor = bottomView?.backgroundColor
} else {
view.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
}
} else { } else {
bottomConstraint?.isActive = true bottomConstraint?.isActive = true
var y: CGFloat? var y: CGFloat?

View File

@ -45,7 +45,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
return return
} }
if #available(iOS 11.0, *), scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic { if scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic {
heightConstraint?.constant = -scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom heightConstraint?.constant = -scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom
} else { } else {
heightConstraint?.constant = -scrollView.contentInset.top - scrollView.contentInset.bottom heightConstraint?.constant = -scrollView.contentInset.top - scrollView.contentInset.bottom
@ -233,14 +233,10 @@ extension ThreeLayerViewController {
view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
if #available(iOS 11.0, *) { parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) {
if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) { safeAreaView.backgroundColor = bottomView?.backgroundColor
safeAreaView.backgroundColor = bottomView?.backgroundColor self.safeAreaView = safeAreaView
self.safeAreaView = safeAreaView
}
} else {
NSLayoutConstraint.pinViewBottom(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
} }
} }
} }

View File

@ -283,16 +283,6 @@
// So we will update titles. // So we will update titles.
[self newDataBuildScreen]; [self newDataBuildScreen];
// Fix for right bar button item with custom view which disappears when user navigates to top tabbar page controller
if (@available(iOS 11.0, *)) {
} else {
NSMutableArray *buttonItems = [[NSMutableArray alloc] init];
UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
[buttonItems addObject:space];
[buttonItems addObjectsFromArray:self.navigationItem.rightBarButtonItems];
self.navigationItem.rightBarButtonItems = buttonItems;
}
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {

View File

@ -325,19 +325,13 @@
NSLayoutConstraint *bottomViewTop = [NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:tableView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]; NSLayoutConstraint *bottomViewTop = [NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:tableView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
bottomViewTop.active = YES; bottomViewTop.active = YES;
NSLayoutConstraint *bottomViewBot = nil; NSLayoutConstraint *bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor];
if (@available(iOS 11.0, *)) { bottomViewBot.priority = 900;
bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor]; bottomViewBot.active = YES;
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
safeAreaView.backgroundColor = footerView.backgroundColor; safeAreaView.backgroundColor = footerView.backgroundColor;
self.safeAreaView = safeAreaView; self.safeAreaView = safeAreaView;
} else {
// Fallback on earlier versions
bottomViewBot = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:footerView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
}
bottomViewBot.priority = 900;
bottomViewBot.active = YES;
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]]; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]];
} else { } else {

View File

@ -272,13 +272,8 @@
- (void)updateViewConstraints { - (void)updateViewConstraints {
[super updateViewConstraints]; [super updateViewConstraints];
// Updates for ios 11 if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
if (@available(iOS 11.0, *)) { self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom;
if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom;
} else {
self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom;
}
} else { } else {
self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom; self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom;
} }
@ -322,17 +317,13 @@
[self.view addSubview:bottomView]; [self.view addSubview:bottomView];
UIScrollView *scrollview = self.scrollView; UIScrollView *scrollview = self.scrollView;
if (@available(iOS 11.0, *)) { [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]]; [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES;
[self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES;
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; safeAreaView.backgroundColor = bottomView.backgroundColor;
safeAreaView.backgroundColor = bottomView.backgroundColor; self.safeAreaView = safeAreaView;
self.safeAreaView = safeAreaView;
} else {
// Fallback on earlier versions
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
}
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[bottomView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(bottomView)]]; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[bottomView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(bottomView)]];
} }

View File

@ -37,11 +37,9 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi
} }
isAccessibilityElement = false isAccessibilityElement = false
contentView.isAccessibilityElement = false contentView.isAccessibilityElement = false
if #available(iOS 11.0, *) { insetsLayoutMarginsFromSafeArea = false
insetsLayoutMarginsFromSafeArea = false contentView.insetsLayoutMarginsFromSafeArea = false
contentView.insetsLayoutMarginsFromSafeArea = false contentView.preservesSuperviewLayoutMargins = false
contentView.preservesSuperviewLayoutMargins = false
}
// Covers the card when peaking. // Covers the card when peaking.
peakingCover.backgroundColor = .white peakingCover.backgroundColor = .white

View File

@ -53,6 +53,17 @@ import UIKit
} }
} }
open override func layoutSubviews() {
super.layoutSubviews()
// Ensures accessory view aligns to the center y derived from the
if let center = heroAccessoryCenter {
accessoryView?.center.y = center.y
}
}
var heroAccessoryCenter: CGPoint?
func styleStandard() { func styleStandard() {
topMarginPadding = 24 topMarginPadding = 24
bottomMarginPadding = 24 bottomMarginPadding = 24
@ -73,6 +84,11 @@ import UIKit
bottomSeparatorView?.hide() bottomSeparatorView?.hide()
} }
public func willDisplay() {
alignAccessoryToHero()
}
// MARK: - Inits // MARK: - Inits
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
@ -87,29 +103,16 @@ import UIKit
// MARK: - MFViewProtocol // MARK: - MFViewProtocol
public func updateView(_ size: CGFloat) { public func updateView(_ size: CGFloat) {
MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding) MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding)
if #available(iOS 11.0, *) { if accessoryView != nil {
if accessoryView != nil { // Smaller left margin if accessory view.
// Smaller left margin if accessory view. var margin = directionalLayoutMargins
var margin = directionalLayoutMargins margin.trailing = 16
margin.trailing = 16 contentView.directionalLayoutMargins = margin
contentView.directionalLayoutMargins = margin
} else {
contentView.directionalLayoutMargins = directionalLayoutMargins
}
topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
} else { } else {
if accessoryView != nil { contentView.directionalLayoutMargins = directionalLayoutMargins
// Smaller left margin if accessory view.
var margin = layoutMargins
margin.right = 16
contentView.layoutMargins = margin
} else {
contentView.layoutMargins = layoutMargins
}
topSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left)
bottomSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left)
} }
topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
molecule?.updateView(size) molecule?.updateView(size)
if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject { if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject {
@ -121,22 +124,52 @@ import UIKit
public func setupView() { public func setupView() {
selectionStyle = .none selectionStyle = .none
if #available(iOS 11.0, *) { insetsLayoutMarginsFromSafeArea = false
insetsLayoutMarginsFromSafeArea = false contentView.insetsLayoutMarginsFromSafeArea = false
contentView.insetsLayoutMarginsFromSafeArea = false contentView.preservesSuperviewLayoutMargins = false
contentView.preservesSuperviewLayoutMargins = false }
/// NOTE: Should only be called when displayed or about to be displayed.
public func alignAccessoryToHero() {
// Layout call required to force draw in memory to get dimensions of subviews.
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)
accessoryView?.center.y = contentView.convert(UIView(frame: rect).center, from: heroLabel).y
heroAccessoryCenter = accessoryView?.center
}
/// Traverses 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 +194,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)
@ -214,10 +246,8 @@ 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() { @objc 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,7 +24,6 @@
/// 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;
/// 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

@ -17,4 +17,6 @@
/// Handle action /// Handle action
- (void)didSelectCellAtIndex:(nonnull NSIndexPath *)indexPath delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData; - (void)didSelectCellAtIndex:(nonnull NSIndexPath *)indexPath delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
- (void)willDisplay;
@end @end

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)
} }
@ -88,6 +87,13 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
return cell return cell
} }
open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let protocolCell = cell as? MoleculeListCellProtocol {
protocolCell.willDisplay?()
}
}
open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol { if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol {
cell.didSelectCell?(atIndex: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) cell.didSelectCell?(atIndex: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil)
@ -120,9 +126,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
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) {

View File

@ -198,13 +198,8 @@ static const CGFloat VertialShadowOffset = 6;
[view.rightAnchor constraintEqualToAnchor:button.rightAnchor constant:PaddingTwo].active = YES; [view.rightAnchor constraintEqualToAnchor:button.rightAnchor constant:PaddingTwo].active = YES;
[view.centerYAnchor constraintEqualToAnchor:button.centerYAnchor].active = YES; [view.centerYAnchor constraintEqualToAnchor:button.centerYAnchor].active = YES;
} else { } else {
if (@available(iOS 11.0, *)) { [button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES;
[button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES; [view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES;
[view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES;
} else {
[NSLayoutConstraint constraintPinSubview:button pinTop:YES topConstant:PaddingOne pinBottom:NO bottomConstant:0 pinLeft:NO leftConstant:0 pinRight:YES rightConstant:PaddingTwo];
}
} }
} }
return button; return button;
@ -249,16 +244,12 @@ static const CGFloat VertialShadowOffset = 6;
} }
+ (nullable UIView *)getAndSetupSafeAreaViewOnView:(nonnull UIView *)view { + (nullable UIView *)getAndSetupSafeAreaViewOnView:(nonnull UIView *)view {
if (@available(iOS 11.0, *)) { UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView];
UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView]; [view addSubview:safeAreaView];
[view addSubview:safeAreaView]; [safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES;
[safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES; [view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES;
[view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES; [NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
[NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0]; return safeAreaView;
return safeAreaView;
} else {
return nil;
}
} }
#pragma mark - shadows #pragma mark - shadows

View File

@ -58,21 +58,13 @@
} }
+ (UIEdgeInsets)getMarginsForView:(nullable UIView *)view { + (UIEdgeInsets)getMarginsForView:(nullable UIView *)view {
if (@available(iOS 11.0, *)) { return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing);
return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing);
} else {
return view.layoutMargins;
}
} }
#pragma mark - Setters #pragma mark - Setters
+ (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom { + (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom {
if (@available(iOS 11.0, *)) { view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing);
view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing);
} else {
view.layoutMargins = UIEdgeInsetsMake(top, leading, bottom, trailing);
}
} }
#pragma mark - Formatting #pragma mark - Formatting
@ -152,12 +144,9 @@
CGFloat topInset = scrollview.contentInset.top; CGFloat topInset = scrollview.contentInset.top;
CGFloat bottomInset = scrollview.contentInset.bottom; CGFloat bottomInset = scrollview.contentInset.bottom;
// Updates for ios 11 if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
if (@available(iOS 11.0, *)) { topInset = scrollview.adjustedContentInset.top;
if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) { bottomInset = scrollview.adjustedContentInset.bottom;
topInset = scrollview.adjustedContentInset.top;
bottomInset = scrollview.adjustedContentInset.bottom;
}
} }
CGFloat remainingSpace = frameHeight - contentSizeHeight - topInset - bottomInset; CGFloat remainingSpace = frameHeight - contentSizeHeight - topInset - bottomInset;