From f771469bc5b067712ed041c624ff2a63dcc7350d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Sep 2020 09:11:19 -0400 Subject: [PATCH] Molecular top alerts --- MVMCoreUI.xcodeproj/project.pbxproj | 12 +- .../CollapsableNotification.swift | 162 ++++++++++-------- ...t => CollapsableNotificationTopView.swift} | 11 +- .../TopNotification/Notification.swift | 55 +----- .../Protocols/AccessibilityProtocol.swift | 14 ++ .../MVMCoreUISplitViewController.m | 11 +- MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 18 +- MVMCoreUI/Utility/MVMCoreUIUtility.m | 6 +- 8 files changed, 155 insertions(+), 134 deletions(-) rename MVMCoreUI/Atomic/Molecules/TopNotification/{NotificationStatusBar.swift => CollapsableNotificationTopView.swift} (82%) create mode 100644 MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index de7e8645..404ae487 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -484,9 +484,10 @@ D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; }; D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; }; D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; }; - D2FA83D62515021F00564112 /* NotificationStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* NotificationStatusBar.swift */; }; + D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; }; + D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */; }; DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; @@ -977,9 +978,10 @@ D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = ""; }; D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = ""; }; D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = ""; }; - D2FA83D52515021F00564112 /* NotificationStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationStatusBar.swift; sourceTree = ""; }; + D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationTopView.swift; sourceTree = ""; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = ""; }; D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = ""; }; + D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityProtocol.swift; sourceTree = ""; }; DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = ""; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; @@ -1067,6 +1069,7 @@ children = ( D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */, 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, + D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */, ); path = Protocols; sourceTree = ""; @@ -2091,7 +2094,7 @@ D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */, D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */, D2FA83D32514F80C00564112 /* CollapsableNotification.swift */, - D2FA83D52515021F00564112 /* NotificationStatusBar.swift */, + D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */, D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */, ); path = TopNotification; @@ -2400,6 +2403,7 @@ AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */, 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */, + D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */, D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */, D20923592450ECE00044AD09 /* TableView.swift in Sources */, @@ -2574,7 +2578,7 @@ AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */, - D2FA83D62515021F00564112 /* NotificationStatusBar.swift in Sources */, + D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */, 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */, AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */, D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index 54eeb51a..6c8d84b8 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -13,7 +13,7 @@ import Foundation // MARK: - Outlets //-------------------------------------------------- - public let topView = NotificationStatusBar() + public let topView = CollapsableNotificationTopView() public let bottomView = NotificationView() public var verticalStack: UIStackView! @@ -53,108 +53,112 @@ import Foundation open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? CollapsableNotificationModel else { return } + topView.label.set(with: model.topLabel, delegateObject, additionalData) topView.button.set(with: model.topAction, delegateObject: delegateObject, additionalData: additionalData) + topView.updateAccessibility() + bottomView.set(with: model, delegateObject, additionalData) - updateAccessibilityLabel() + // Set initial collapse/expand state. topView.isHidden = !model.alwaysShowTopLabel && !model.initiallyCollapsed topView.button.isUserInteractionEnabled = model.initiallyCollapsed bottomView.isHidden = model.initiallyCollapsed verticalStack.layoutIfNeeded() if !model.initiallyCollapsed { - collapse(with: .now() + DispatchTimeInterval.seconds(model.collapseTime)) + autoCollapse() } } - open func collapse(with delay: DispatchTime) { - DispatchQueue.main.asyncAfter(deadline: delay) { [weak self] in - self?.collapse() + open func performBlockOperation(with block: @escaping (MVMCoreBlockOperation) -> Void) { + let operation = MVMCoreBlockOperation(block: block)! + MVMCoreNavigationHandler.shared()?.addNavigationOperation(operation) + } + + /// Collapses after a delay + open func autoCollapse() { + let delay: DispatchTimeInterval = DispatchTimeInterval.seconds((model as? CollapsableNotificationModel)?.collapseTime ?? 5) + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in + // If accessibility focused, delay collapse. + guard let localSelf = self else { return } + if MVMCoreUIUtility.viewContainsAccessiblityFocus(localSelf) { + NotificationCenter.default.addObserver(localSelf, selector: #selector(localSelf.accessibilityFocusChanged(notification:)), name: UIAccessibility.elementFocusedNotification, object: nil) + } else { + localSelf.collapse() + } } } + /// Collapses to show just the top view. open func collapse(animated: Bool = true) { - let animation = { [weak self] in - self?.topView.isHidden = false - self?.bottomView.isHidden = true - self?.verticalStack.layoutIfNeeded() - } - if animated { - UIView.animate(withDuration: 0.5, animations: animation) { [weak self] (finished) in - self?.topView.button.isUserInteractionEnabled = true - } - } else { - animation() + guard !bottomView.isHidden else { return } + performBlockOperation { [weak self] (operation) in + let strongSelf = self + MVMCoreDispatchUtility.performBlock(onMainThread: { + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + let animation = { + strongSelf?.topView.isHidden = false + strongSelf?.bottomView.isHidden = true + strongSelf?.verticalStack.layoutIfNeeded() + } + let completion: (Bool) -> Void = { (finished) in + strongSelf?.topView.button.isUserInteractionEnabled = true + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument()) + operation.markAsFinished() + } + + if animated { + UIView.animate(withDuration: 0.5, animations: animation, completion: completion) + } else { + animation() + completion(true) + } + }) } } + /// Expands to show the bottom view. open func expand(topViewShowing: Bool = false, animated: Bool = true) { - topView.button.isUserInteractionEnabled = false - let animation = { [weak self] in - self?.topView.isHidden = !topViewShowing - self?.bottomView.isHidden = false - self?.verticalStack.layoutIfNeeded() - } - if animated { - UIView.animate(withDuration: 0.5, animations: animation) { [weak self] (finished) in + guard bottomView.isHidden else { return } + performBlockOperation { [weak self] (operation) in + let strongSelf = self + MVMCoreDispatchUtility.performBlock(onMainThread: { + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + strongSelf?.topView.button.isUserInteractionEnabled = false + let animation = { + strongSelf?.topView.isHidden = !topViewShowing + strongSelf?.bottomView.isHidden = false + strongSelf?.verticalStack.layoutIfNeeded() + } + let completion: (Bool) -> Void = { (finished) in + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument()) + strongSelf?.autoCollapse() + operation.markAsFinished() + } - } - } else { - animation() + if animated { + UIView.animate(withDuration: 0.5, animations: animation, completion: completion) + } else { + animation() + completion(true) + } + }) } } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 96 } - /* - func getAccessibilityMessage() -> String? { - - guard let leftImageLabel = leftImage.imageView.accessibilityLabel else { - return eyebrowHeadlineBodyLink.getAccessibilityMessage() + + /// Collapse if focus is no longer on this top alert. + @objc func accessibilityFocusChanged(notification: Notification) { + if !MVMCoreUIUtility.viewContainsAccessiblityFocus(self) { + NotificationCenter.default.removeObserver(self, name: UIAccessibility.elementFocusedNotification, object: nil) + collapse() } - - guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { - return leftImageLabel - } - - return leftImageLabel + ", " + label - }*/ - - func updateAccessibilityLabel() { - /*headline.accessibilityLabel = headline.text - MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline) - - body.accessibilityLabel = body.text - MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body) - - - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 - isAccessibilityElement = !linkShowing - accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none - - if !linkShowing { - // Make whole cell focusable if no link. - accessibilityLabel = getAccessibilityMessage() - } else if let accessoryView = accessoryView { - // Both caret and link. Read all content on caret. - accessoryView.accessibilityLabel = getAccessibilityMessage() - accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] - } else { - // Only link. Manually add accessibility elements to ensure they are read in the right order. - var elements: [Any] = [] - - if let leftImageLabel = leftImage.imageView.accessibilityLabel, !leftImageLabel.isEmpty { - elements.append(leftImage.imageView) - } - - if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { - elements.append(otherElements) - } - - accessibilityElements = elements - }*/ } } @@ -166,3 +170,13 @@ extension CollapsableNotification: StatusBarUI { return (color, greyScale > 0.5 ? .lightContent : .default) } } + +extension CollapsableNotification: AccessibilityProtocol { + public func getAccessibilityLayoutChangedArgument() -> Any? { + if !topView.isHidden { + return topView + } else { + return bottomView.headline + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift similarity index 82% rename from MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift rename to MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift index 73bde3f1..36ddc8a6 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift @@ -1,5 +1,5 @@ // -// NotificationStatusBar.swift +// CollapsableNotificationTopView.swift // MVMCoreUI // // Created by Scott Pfeil on 9/18/20. @@ -8,7 +8,7 @@ import Foundation -@objcMembers open class NotificationStatusBar: View { +@objcMembers open class CollapsableNotificationTopView: View { public let label: Label = { let label = Label(fontStyle: .BoldBodySmall) label.numberOfLines = 1 @@ -50,6 +50,13 @@ import Foundation label.textAlignment = .center } + open func updateAccessibility() { + isAccessibilityElement = true + accessibilityLabel = label.text + accessibilityTraits = (button.isUserInteractionEnabled && button.actionModel != nil) ? .button : .none + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: self) + } + @objc func pressed(_ sender: Notification) { button.callActionBlock(button) } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift index 823158fc..c7d869c2 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift @@ -59,59 +59,22 @@ import Foundation guard let model = model as? NotificationModel else { return } labelStack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, nil) horizontalStack.updateContainedMolecules(with: [labelStack.stackModel, model.button, model.closeButton], delegateObject, nil) - - updateAccessibilityLabel() + updateAccessibility() } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 96 } - /* - func getAccessibilityMessage() -> String? { - - guard let leftImageLabel = leftImage.imageView.accessibilityLabel else { - return eyebrowHeadlineBodyLink.getAccessibilityMessage() - } - - guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { - return leftImageLabel - } - - return leftImageLabel + ", " + label - }*/ - func updateAccessibilityLabel() { - /*headline.accessibilityLabel = headline.text + open func updateAccessibility() { MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline) - - body.accessibilityLabel = body.text MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body) - - - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 - isAccessibilityElement = !linkShowing - accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none - - if !linkShowing { - // Make whole cell focusable if no link. - accessibilityLabel = getAccessibilityMessage() - } else if let accessoryView = accessoryView { - // Both caret and link. Read all content on caret. - accessoryView.accessibilityLabel = getAccessibilityMessage() - accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] - } else { - // Only link. Manually add accessibility elements to ensure they are read in the right order. - var elements: [Any] = [] - - if let leftImageLabel = leftImage.imageView.accessibilityLabel, !leftImageLabel.isEmpty { - elements.append(leftImage.imageView) - } - - if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { - elements.append(otherElements) - } - - accessibilityElements = elements - }*/ + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: button) + } +} + +extension NotificationView: AccessibilityProtocol { + public func getAccessibilityLayoutChangedArgument() -> Any? { + return headline } } diff --git a/MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift b/MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift new file mode 100644 index 00000000..6c7ada50 --- /dev/null +++ b/MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift @@ -0,0 +1,14 @@ +// +// AccessibilityProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/21/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objc public protocol AccessibilityProtocol { + /// Should return the argument to use for posting a layout change. + func getAccessibilityLayoutChangedArgument() -> Any? +} diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 06e44c73..288878b8 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -1073,10 +1073,13 @@ CGFloat const PanelAnimationDuration = 0.2; } - (UIViewController *)getCurrentDetailViewController { - UIViewController *viewController = self.navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + __block UIViewController *viewController = nil; + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ + viewController = self.navigationController.topViewController; + if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { + viewController = [viewController performSelector:@selector(getCurrentViewController)]; + } + }]; return viewController; } diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 570b9297..31899d3f 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -187,6 +187,19 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; [[MVMCoreUISession sharedGlobal].splitViewController.parentViewController setNeedsStatusBarAppearanceUpdate]; } +- (void)updateAccessibilityForTopAlert:(nullable UIView *)view { + // Update accessibility with top alert + if ([view isKindOfClass:[MVMCoreUITopAlertBaseView class]]) { + [((MVMCoreUITopAlertBaseView *)view) handleAccessibility]; + } else { + id accessibilityArgument = view; + if ([view conformsToProtocol:@protocol(AccessibilityProtocol)]) { + accessibilityArgument = [((id )view) getAccessibilityLayoutChangedArgument]; + } + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, accessibilityArgument); + } +} + - (void)showAlertView:(nullable UIView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { __weak typeof(self) weakSelf = self; @@ -207,9 +220,8 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; } completion:^(BOOL finished) { [weakSelf.superview layoutIfNeeded]; [weakSelf.animationDelegate topAlertViewFinishAnimation]; - if ([view isKindOfClass:[MVMCoreUITopAlertBaseView class]]) { - [((MVMCoreUITopAlertBaseView *)view) handleAccessibility]; - } + + [weakSelf updateAccessibilityForTopAlert:view]; [MVMCoreDispatchUtility performBlockInBackground:^{ if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewShown:topAlertObject:)]) { diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index 60ff4872..fed0bb30 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -89,7 +89,11 @@ if (![focusedElement isKindOfClass:[UIView class]]) { return NO; } - return [(UIView *)focusedElement isDescendantOfView:view]; + __block BOOL containsFocus; + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ + containsFocus = [(UIView *)focusedElement isDescendantOfView:view]; + }]; + return containsFocus; } #pragma mark - Setters