From adc73fb927b696ee25662690f912f752d8d404f1 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Tue, 27 Nov 2018 23:48:07 +0900 Subject: [PATCH 01/13] change animation of overlayviewcontroller --- iOS Sample/iOS Sample/OverlayMenuCell.swift | 81 +++++++++++++++---- .../iOS Sample/OverlayViewController.swift | 36 +++++---- 2 files changed, 83 insertions(+), 34 deletions(-) diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index b994255..09b651f 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -27,29 +27,76 @@ import PagingKit class OverlayMenuCell: PagingMenuViewCell { static let sizingCell = UINib(nibName: "OverlayMenuCell", bundle: nil).instantiate(withOwner: self, options: nil).first as! OverlayMenuCell - + let maskLayer: CAShapeLayer = { + let layer = CAShapeLayer() + layer.fillColor = UIColor.black.cgColor + return layer + }() + + let highlightTextLayer: CATextLayer = { + let layer = CATextLayer() + let font = UIFont.systemFont(ofSize: 16) + layer.font = font + layer.fontSize = font.pointSize + layer.foregroundColor = UIColor.white.cgColor + layer.contentsScale = UIScreen.main.scale + layer.alignmentMode = .center + return layer + }() + + let baseTextLayer: CATextLayer = { + let layer = CATextLayer() + let font = UIFont.systemFont(ofSize: 16) + layer.font = font + layer.fontSize = font.pointSize + layer.foregroundColor = UIColor.black.cgColor + layer.contentsScale = UIScreen.main.scale + layer.alignmentMode = .center + return layer + }() + @IBOutlet weak var textLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + textLabel.isHidden = true + maskLayer.isHidden = true + layer.addSublayer(baseTextLayer) + highlightTextLayer.mask = maskLayer + layer.addSublayer(highlightTextLayer) + } - var isHighlight: Bool = false { - didSet { - if isHighlight { - black(percent: 0) - } else { - black(percent: 1) - } + override func layoutSubviews() { + super.layoutSubviews() + if let string = highlightTextLayer.string as? NSString, let font = highlightTextLayer.font as? UIFont { + let stringBounds = string.boundingRect(with: CGSize(width: .max, height: .min), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font], context: nil) + highlightTextLayer.frame = stringBounds + highlightTextLayer.frame.origin.y = (bounds.height - stringBounds.height) / 2 + highlightTextLayer.frame.origin.x = (bounds.width - stringBounds.width) / 2 } + if let string = baseTextLayer.string as? NSString, let font = baseTextLayer.font as? UIFont { + let stringBounds = string.boundingRect(with: CGSize(width: .max, height: .min), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font], context: nil) + baseTextLayer.frame = stringBounds + baseTextLayer.frame.origin.y = (bounds.height - stringBounds.height) / 2 + baseTextLayer.frame.origin.x = (bounds.width - stringBounds.width) / 2 + } + + maskLayer.bounds = bounds.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) + maskLayer.path = UIBezierPath(roundedRect: maskLayer.bounds, cornerRadius: bounds.height / 2).cgPath + } - func black(percent: CGFloat) { - let whiteRatio = 1 - percent - textLabel.textColor = UIColor(white: whiteRatio, alpha: 1) + private func layoutMaskLayer(frame: CGRect) { + maskLayer.frame = frame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) + maskLayer.path = UIBezierPath(roundedRect: maskLayer.bounds, cornerRadius: bounds.height / 2).cgPath } + - func highlightWithAnimation(isHighlight: Bool) { - UIView.transition(with: textLabel, duration: 0.4, options: .transitionCrossDissolve, animations: { - self.textLabel.textColor = isHighlight ? .white : .black - }, completion: { (_) in - - }) + func setFocusViewFrame(frame: CGRect, from view: UIView) { + maskLayer.isHidden = false + let convertedFrame = view.layer.convert(frame, to: highlightTextLayer) + CATransaction.setDisableActions(true) + layoutMaskLayer(frame: convertedFrame) + CATransaction.setDisableActions(false) } } diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index f4b3809..b22d532 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -36,14 +36,16 @@ class OverlayViewController: UIViewController { var menuViewController: PagingMenuViewController! var contentViewController: PagingContentViewController! + var focusView: OverlayFocusView! + override func viewDidLoad() { super.viewDidLoad() - + focusView = UINib(nibName: "OverlayFocusView", bundle: nil).instantiate(withOwner: self, options: nil).first as? OverlayFocusView menuViewController?.register(nib: UINib(nibName: "OverlayMenuCell", bundle: nil), forCellWithReuseIdentifier: "identifier") - menuViewController?.registerFocusView(nib: UINib(nibName: "OverlayFocusView", bundle: nil), isBehindCell: true) - menuViewController?.reloadData(with: 0, completionHandler: { [weak self] (_) in - let cell = self?.menuViewController.currentFocusedCell as? OverlayMenuCell - cell?.isHighlight = true + menuViewController?.registerFocusView(view: focusView, isBehindCell: true) + menuViewController?.reloadData(with: 0, completionHandler: { [weak self, focusView = focusView!] (vc) in + let cell = self?.menuViewController.currentFocusedCell as! OverlayMenuCell + cell.setFocusViewFrame(frame: focusView.frame, from: focusView) }) contentViewController?.reloadData(with: 0) } @@ -71,8 +73,8 @@ class OverlayViewController: UIViewController { extension OverlayViewController: PagingMenuViewControllerDataSource { func menuViewController(viewController: PagingMenuViewController, cellForItemAt index: Int) -> PagingMenuViewCell { let cell = viewController.dequeueReusableCell(withReuseIdentifier: "identifier", for: index) as! OverlayMenuCell - cell.textLabel.text = dataSource[index].menu - cell.isHighlight = viewController.currentFocusedIndex == index + cell.highlightTextLayer.string = dataSource[index].menu + cell.baseTextLayer.string = dataSource[index].menu return cell } @@ -103,22 +105,22 @@ extension OverlayViewController: PagingContentViewControllerDataSource { extension OverlayViewController: PagingMenuViewControllerDelegate { func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) { - viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { $0.highlightWithAnimation(isHighlight: false) } - let selectedCell = menuViewController.cellForItem(at: page) as? OverlayMenuCell - selectedCell?.highlightWithAnimation(isHighlight: true) + viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in + UIView.perform(.delete, on: [], options: UIView.AnimationOptions.curveEaseInOut, animations: { [viewController] in + cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!) + }, completion: nil) + } contentViewController?.scroll(to: page, animated: true) + } } extension OverlayViewController: PagingContentViewControllerDelegate { func contentViewController(viewController: PagingContentViewController, didManualScrollOn index: Int, percent: CGFloat) { - if percent < 0.5 { - let cell = menuViewController.cellForItem(at: index) as? OverlayMenuCell - cell?.black(percent: percent * 2) - } else { - let cell = menuViewController.cellForItem(at: index + 1) as? OverlayMenuCell - cell?.black(percent: (1 - percent) * 2) - } menuViewController?.scroll(index: index, percent: percent, animated: false) + menuViewController.visibleCells.forEach { + let cell = $0 as! OverlayMenuCell + cell.setFocusViewFrame(frame: focusView.bounds, from: focusView) + } } } From b6c7973994e2e1035400751c53576a80bd12a396 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Wed, 28 Nov 2018 23:38:48 +0900 Subject: [PATCH 02/13] add select animation --- iOS Sample/iOS Sample/OverlayMenuCell.swift | 37 +++++++++++++++++-- .../iOS Sample/OverlayViewController.swift | 8 ++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index 09b651f..6bbc3c9 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -92,11 +92,40 @@ class OverlayMenuCell: PagingMenuViewCell { } - func setFocusViewFrame(frame: CGRect, from view: UIView) { + private func animateLayoutMaskLayer(frame: CGRect, fromFrame: CGRect) { + let _frame = frame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) + let _fromFrame = fromFrame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) + + let positionAnimation = CABasicAnimation(keyPath: "position.x") + positionAnimation.fromValue = NSNumber(value: Double(_fromFrame.midX)) + positionAnimation.toValue = NSNumber(value: Double(_frame.midX)) + positionAnimation.duration = 0.325 + positionAnimation.fillMode = .forwards + positionAnimation.isRemovedOnCompletion = false + positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + maskLayer.add(positionAnimation, forKey: "position.x") + + let widthAnimation = CABasicAnimation(keyPath: "bounds.size.width") + widthAnimation.fromValue = NSNumber(value: Double(_fromFrame.width)) + widthAnimation.toValue = NSNumber(value: Double(_frame.width)) + widthAnimation.duration = 0.325 + widthAnimation.fillMode = .forwards + widthAnimation.isRemovedOnCompletion = false + widthAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + maskLayer.add(widthAnimation, forKey: "bounds.size.width") + + } + + func setFocusViewFrame(frame: CGRect, from view: UIView, baseView: UIView? = nil, animated: Bool) { maskLayer.isHidden = false let convertedFrame = view.layer.convert(frame, to: highlightTextLayer) - CATransaction.setDisableActions(true) - layoutMaskLayer(frame: convertedFrame) - CATransaction.setDisableActions(false) + if animated { + let fromFrame = baseView!.layer.convert(baseView!.layer.bounds, to: highlightTextLayer) + animateLayoutMaskLayer(frame: convertedFrame, fromFrame: fromFrame) + } else { + CATransaction.setDisableActions(true) + layoutMaskLayer(frame: convertedFrame) + CATransaction.setDisableActions(false) + } } } diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index b22d532..cbc5963 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -45,7 +45,7 @@ class OverlayViewController: UIViewController { menuViewController?.registerFocusView(view: focusView, isBehindCell: true) menuViewController?.reloadData(with: 0, completionHandler: { [weak self, focusView = focusView!] (vc) in let cell = self?.menuViewController.currentFocusedCell as! OverlayMenuCell - cell.setFocusViewFrame(frame: focusView.frame, from: focusView) + cell.setFocusViewFrame(frame: focusView.frame, from: focusView, animated: false) }) contentViewController?.reloadData(with: 0) } @@ -106,9 +106,7 @@ extension OverlayViewController: PagingContentViewControllerDataSource { extension OverlayViewController: PagingMenuViewControllerDelegate { func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) { viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - UIView.perform(.delete, on: [], options: UIView.AnimationOptions.curveEaseInOut, animations: { [viewController] in - cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!) - }, completion: nil) + cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!, baseView: viewController.cellForItem(at: previousPage)!, animated: true) } contentViewController?.scroll(to: page, animated: true) @@ -120,7 +118,7 @@ extension OverlayViewController: PagingContentViewControllerDelegate { menuViewController?.scroll(index: index, percent: percent, animated: false) menuViewController.visibleCells.forEach { let cell = $0 as! OverlayMenuCell - cell.setFocusViewFrame(frame: focusView.bounds, from: focusView) + cell.setFocusViewFrame(frame: focusView.bounds, from: focusView, animated: false) } } } From 61319f01ca34ee951a7f499566864d419ca645eb Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Thu, 29 Nov 2018 23:52:21 +0900 Subject: [PATCH 03/13] fix animation --- iOS Sample/iOS Sample/OverlayMenuCell.swift | 32 ++++++++++----------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index 6bbc3c9..04a6d0c 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -93,27 +93,25 @@ class OverlayMenuCell: PagingMenuViewCell { private func animateLayoutMaskLayer(frame: CGRect, fromFrame: CGRect) { - let _frame = frame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) - let _fromFrame = fromFrame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) - - let positionAnimation = CABasicAnimation(keyPath: "position.x") - positionAnimation.fromValue = NSNumber(value: Double(_fromFrame.midX)) - positionAnimation.toValue = NSNumber(value: Double(_frame.midX)) - positionAnimation.duration = 0.325 - positionAnimation.fillMode = .forwards - positionAnimation.isRemovedOnCompletion = false + let positionAnimation = CABasicAnimation(keyPath: "position") + positionAnimation.fromValue = NSValue(cgPoint: CGPoint(x: fromFrame.midX, y: fromFrame.midY)) + positionAnimation.toValue = NSValue(cgPoint: CGPoint(x: frame.midX, y: frame.midY)) positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - maskLayer.add(positionAnimation, forKey: "position.x") let widthAnimation = CABasicAnimation(keyPath: "bounds.size.width") - widthAnimation.fromValue = NSNumber(value: Double(_fromFrame.width)) - widthAnimation.toValue = NSNumber(value: Double(_frame.width)) - widthAnimation.duration = 0.325 - widthAnimation.fillMode = .forwards - widthAnimation.isRemovedOnCompletion = false - widthAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - maskLayer.add(widthAnimation, forKey: "bounds.size.width") + widthAnimation.fromValue = NSNumber(value: Double(fromFrame.width)) + widthAnimation.toValue = NSNumber(value: Double(frame.width)) + positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + CATransaction.begin() + let groupAnimation = CAAnimationGroup() + groupAnimation.fillMode = .forwards + groupAnimation.isRemovedOnCompletion = false + groupAnimation.duration = 0.325 + groupAnimation.animations = [positionAnimation, widthAnimation] + maskLayer.add(groupAnimation, forKey: "group") + CATransaction.commit() + } func setFocusViewFrame(frame: CGRect, from view: UIView, baseView: UIView? = nil, animated: Bool) { From 5f13183a65353269a9bc35f75affa5bae34daa3b Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Fri, 30 Nov 2018 00:07:51 +0900 Subject: [PATCH 04/13] remove Animations --- iOS Sample/iOS Sample/OverlayMenuCell.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index 04a6d0c..91e0dec 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -121,6 +121,7 @@ class OverlayMenuCell: PagingMenuViewCell { let fromFrame = baseView!.layer.convert(baseView!.layer.bounds, to: highlightTextLayer) animateLayoutMaskLayer(frame: convertedFrame, fromFrame: fromFrame) } else { + maskLayer.removeAllAnimations() CATransaction.setDisableActions(true) layoutMaskLayer(frame: convertedFrame) CATransaction.setDisableActions(false) From 2dd5a77391e96b0b861365c833379f9b4e62679b Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 00:25:13 +0900 Subject: [PATCH 05/13] add animation coordinator to focusview --- PagingKit/PagingMenuView.swift | 11 ++++++++++ PagingKit/PagingMenuViewController.swift | 8 +++++++ iOS Sample/iOS Sample/OverlayMenuCell.swift | 21 ++----------------- .../iOS Sample/OverlayViewController.swift | 18 +++++++++++++--- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/PagingKit/PagingMenuView.swift b/PagingKit/PagingMenuView.swift index 7ccf5c5..0212e9e 100644 --- a/PagingKit/PagingMenuView.swift +++ b/PagingKit/PagingMenuView.swift @@ -66,6 +66,8 @@ open class PagingMenuFocusView: UIView { required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } + + public var coordinator: (() -> Void)? } /** @@ -110,6 +112,12 @@ public protocol PagingMenuViewDelegate: class { /// - pagingMenuView: The paging menu view requesting this information. /// - index: The index that specifies the location of the item. func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) + func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView) +} + +public extension PagingMenuViewDelegate { + public func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) {} + public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView) {} } /// Displays menu lists of information and supports selection and paging of the information. @@ -372,10 +380,13 @@ open class PagingMenuView: UIScrollView { focusView.selectedIndex = index visibleCells.selectCell(with: index) + menuDelegate?.pagingMenuView(pagingMenuView: self, willAnimate: focusView) UIView.perform(.delete, on: [], options: UIView.AnimationOptions(rawValue: 0), animations: { [weak self] in self?.contentOffset = offset self?.focusView.frame = itemFrame self?.focusView.layoutIfNeeded() + self?.focusView.coordinator?() + self?.focusView.coordinator = nil }, completion:completeHandler) } diff --git a/PagingKit/PagingMenuViewController.swift b/PagingKit/PagingMenuViewController.swift index d437624..6475927 100644 --- a/PagingKit/PagingMenuViewController.swift +++ b/PagingKit/PagingMenuViewController.swift @@ -41,10 +41,14 @@ public protocol PagingMenuViewControllerDelegate: class { /// - page: An page number focusing the new selected menu in menu view controller. /// - previousPage: An page number previously focusing menu in menu view controller. func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) + + func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView) } extension PagingMenuViewControllerDelegate { public func menuViewController(viewController: PagingMenuViewController, focusViewDidEndTransition focusView: PagingMenuFocusView) {} + + public func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView) {} } /// The data source provides the paging menu view controller object with the information it needs to construct and modify the menus. @@ -298,6 +302,10 @@ extension PagingMenuViewController: UIScrollViewDelegate { // MARK:- PagingMenuViewDelegate extension PagingMenuViewController: PagingMenuViewDelegate { + public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView) { + delegate?.menuViewController(viewController: self, willAnimate: focusView) + } + public func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) { guard menuView.focusView.selectedIndex != index else { return } diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index 91e0dec..ec66e70 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -93,25 +93,8 @@ class OverlayMenuCell: PagingMenuViewCell { private func animateLayoutMaskLayer(frame: CGRect, fromFrame: CGRect) { - let positionAnimation = CABasicAnimation(keyPath: "position") - positionAnimation.fromValue = NSValue(cgPoint: CGPoint(x: fromFrame.midX, y: fromFrame.midY)) - positionAnimation.toValue = NSValue(cgPoint: CGPoint(x: frame.midX, y: frame.midY)) - positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - - let widthAnimation = CABasicAnimation(keyPath: "bounds.size.width") - widthAnimation.fromValue = NSNumber(value: Double(fromFrame.width)) - widthAnimation.toValue = NSNumber(value: Double(frame.width)) - positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - - CATransaction.begin() - let groupAnimation = CAAnimationGroup() - groupAnimation.fillMode = .forwards - groupAnimation.isRemovedOnCompletion = false - groupAnimation.duration = 0.325 - groupAnimation.animations = [positionAnimation, widthAnimation] - maskLayer.add(groupAnimation, forKey: "group") - CATransaction.commit() - + maskLayer.position = CGPoint(x: frame.midX, y: frame.midY) + maskLayer.bounds.size.width = frame.width } func setFocusViewFrame(frame: CGRect, from view: UIView, baseView: UIView? = nil, animated: Bool) { diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index cbc5963..46dd36e 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -68,6 +68,9 @@ class OverlayViewController: UIViewController { } } + + var page: Int? + var prevPage: Int? } extension OverlayViewController: PagingMenuViewControllerDataSource { @@ -105,12 +108,21 @@ extension OverlayViewController: PagingContentViewControllerDataSource { extension OverlayViewController: PagingMenuViewControllerDelegate { func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) { - viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!, baseView: viewController.cellForItem(at: previousPage)!, animated: true) - } + self.page = page + self.prevPage = previousPage contentViewController?.scroll(to: page, animated: true) } + + func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView) { + focusView.coordinator = { [page = self.page!, prevPage = self.prevPage!] in + viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in + cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!, baseView: viewController.cellForItem(at: prevPage)!, animated: true) + } + } + page = nil + prevPage = nil + } } extension OverlayViewController: PagingContentViewControllerDelegate { From 6cb4001ce6afbcadb8b96d2b3f22fbb3b0440991 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 00:36:33 +0900 Subject: [PATCH 06/13] fix animation --- iOS Sample/iOS Sample/OverlayMenuCell.swift | 21 +++++-------------- .../iOS Sample/OverlayViewController.swift | 10 ++++----- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index ec66e70..8a42850 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -90,24 +90,13 @@ class OverlayMenuCell: PagingMenuViewCell { maskLayer.frame = frame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) maskLayer.path = UIBezierPath(roundedRect: maskLayer.bounds, cornerRadius: bounds.height / 2).cgPath } - - - private func animateLayoutMaskLayer(frame: CGRect, fromFrame: CGRect) { - maskLayer.position = CGPoint(x: frame.midX, y: frame.midY) - maskLayer.bounds.size.width = frame.width - } - func setFocusViewFrame(frame: CGRect, from view: UIView, baseView: UIView? = nil, animated: Bool) { + func setFocusViewFrame(frame: CGRect, from view: UIView, animated: Bool) { maskLayer.isHidden = false let convertedFrame = view.layer.convert(frame, to: highlightTextLayer) - if animated { - let fromFrame = baseView!.layer.convert(baseView!.layer.bounds, to: highlightTextLayer) - animateLayoutMaskLayer(frame: convertedFrame, fromFrame: fromFrame) - } else { - maskLayer.removeAllAnimations() - CATransaction.setDisableActions(true) - layoutMaskLayer(frame: convertedFrame) - CATransaction.setDisableActions(false) - } + + CATransaction.setDisableActions(!animated) + layoutMaskLayer(frame: convertedFrame) + CATransaction.setDisableActions(false) } } diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index 46dd36e..f1bdf30 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -70,7 +70,6 @@ class OverlayViewController: UIViewController { var page: Int? - var prevPage: Int? } extension OverlayViewController: PagingMenuViewControllerDataSource { @@ -78,6 +77,9 @@ extension OverlayViewController: PagingMenuViewControllerDataSource { let cell = viewController.dequeueReusableCell(withReuseIdentifier: "identifier", for: index) as! OverlayMenuCell cell.highlightTextLayer.string = dataSource[index].menu cell.baseTextLayer.string = dataSource[index].menu + if let focusedCell = viewController.currentFocusedCell { + cell.setFocusViewFrame(frame: focusedCell.frame, from: focusedCell, animated: false) + } return cell } @@ -109,19 +111,17 @@ extension OverlayViewController: PagingContentViewControllerDataSource { extension OverlayViewController: PagingMenuViewControllerDelegate { func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) { self.page = page - self.prevPage = previousPage contentViewController?.scroll(to: page, animated: true) } func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView) { - focusView.coordinator = { [page = self.page!, prevPage = self.prevPage!] in + focusView.coordinator = { [page = self.page!] in viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!, baseView: viewController.cellForItem(at: prevPage)!, animated: true) + cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!, animated: true) } } page = nil - prevPage = nil } } From 3c922d2e4c847053c025942e3bf9bc57e9bb51ec Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 00:41:19 +0900 Subject: [PATCH 07/13] removed state --- PagingKit/PagingMenuView.swift | 6 +++--- PagingKit/PagingMenuViewController.swift | 8 ++++---- iOS Sample/iOS Sample/OverlayViewController.swift | 12 ++++-------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/PagingKit/PagingMenuView.swift b/PagingKit/PagingMenuView.swift index 0212e9e..c3cb40f 100644 --- a/PagingKit/PagingMenuView.swift +++ b/PagingKit/PagingMenuView.swift @@ -112,12 +112,12 @@ public protocol PagingMenuViewDelegate: class { /// - pagingMenuView: The paging menu view requesting this information. /// - index: The index that specifies the location of the item. func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) - func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView) + func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView, at index: Int) } public extension PagingMenuViewDelegate { public func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) {} - public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView) {} + public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView, at index: Int) {} } /// Displays menu lists of information and supports selection and paging of the information. @@ -380,7 +380,7 @@ open class PagingMenuView: UIScrollView { focusView.selectedIndex = index visibleCells.selectCell(with: index) - menuDelegate?.pagingMenuView(pagingMenuView: self, willAnimate: focusView) + menuDelegate?.pagingMenuView(pagingMenuView: self, willAnimate: focusView, at: index) UIView.perform(.delete, on: [], options: UIView.AnimationOptions(rawValue: 0), animations: { [weak self] in self?.contentOffset = offset self?.focusView.frame = itemFrame diff --git a/PagingKit/PagingMenuViewController.swift b/PagingKit/PagingMenuViewController.swift index 6475927..8e7ef7f 100644 --- a/PagingKit/PagingMenuViewController.swift +++ b/PagingKit/PagingMenuViewController.swift @@ -42,13 +42,13 @@ public protocol PagingMenuViewControllerDelegate: class { /// - previousPage: An page number previously focusing menu in menu view controller. func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) - func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView) + func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) } extension PagingMenuViewControllerDelegate { public func menuViewController(viewController: PagingMenuViewController, focusViewDidEndTransition focusView: PagingMenuFocusView) {} - public func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView) {} + public func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) {} } /// The data source provides the paging menu view controller object with the information it needs to construct and modify the menus. @@ -302,8 +302,8 @@ extension PagingMenuViewController: UIScrollViewDelegate { // MARK:- PagingMenuViewDelegate extension PagingMenuViewController: PagingMenuViewDelegate { - public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView) { - delegate?.menuViewController(viewController: self, willAnimate: focusView) + public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView, at index: Int) { + delegate?.menuViewController(viewController: self, willAnimate: focusView, at: index) } public func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) { diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index f1bdf30..c240edf 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -67,9 +67,6 @@ class OverlayViewController: UIViewController { contentViewController?.dataSource = self } } - - - var page: Int? } extension OverlayViewController: PagingMenuViewControllerDataSource { @@ -110,18 +107,17 @@ extension OverlayViewController: PagingContentViewControllerDataSource { extension OverlayViewController: PagingMenuViewControllerDelegate { func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) { - self.page = page contentViewController?.scroll(to: page, animated: true) } - func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView) { - focusView.coordinator = { [page = self.page!] in + func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) { + guard let nextCell = viewController.cellForItem(at: index) else { return } + focusView.coordinator = { viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - cell.setFocusViewFrame(frame: viewController.cellForItem(at: page)!.bounds, from: viewController.cellForItem(at: page)!, animated: true) + cell.setFocusViewFrame(frame: nextCell.bounds, from: nextCell, animated: true) } } - page = nil } } From 6efe2188126cff2ed651bc4974544f1b6314c088 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 00:50:11 +0900 Subject: [PATCH 08/13] add animaation coordinator --- PagingKit/PagingMenuView.swift | 22 ++++++++++++++++--- .../iOS Sample/OverlayViewController.swift | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/PagingKit/PagingMenuView.swift b/PagingKit/PagingMenuView.swift index c3cb40f..a024430 100644 --- a/PagingKit/PagingMenuView.swift +++ b/PagingKit/PagingMenuView.swift @@ -54,6 +54,16 @@ open class PagingMenuViewCell: UIView { public internal(set) var index: Int! } +open class PagingMenuFocusViewAnimationCoordinator { + fileprivate var animationHandler: ((PagingMenuFocusViewAnimationCoordinator) -> Void)? + fileprivate var completionHandler: ((Bool) -> Void)? + + open func animateFocusView(alongside animation: @escaping (PagingMenuFocusViewAnimationCoordinator) -> Void, completion: ((Bool) -> Void)?) { + animationHandler = animation + completionHandler = completion + } +} + /// A view that focus menu corresponding to current page. open class PagingMenuFocusView: UIView { open var selectedIndex: Int? @@ -67,7 +77,7 @@ open class PagingMenuFocusView: UIView { super.init(coder: aDecoder) } - public var coordinator: (() -> Void)? + open var coordinator: PagingMenuFocusViewAnimationCoordinator? } /** @@ -380,14 +390,20 @@ open class PagingMenuView: UIScrollView { focusView.selectedIndex = index visibleCells.selectCell(with: index) + focusView.coordinator = PagingMenuFocusViewAnimationCoordinator() menuDelegate?.pagingMenuView(pagingMenuView: self, willAnimate: focusView, at: index) UIView.perform(.delete, on: [], options: UIView.AnimationOptions(rawValue: 0), animations: { [weak self] in self?.contentOffset = offset self?.focusView.frame = itemFrame self?.focusView.layoutIfNeeded() - self?.focusView.coordinator?() + if let coordinator = self?.focusView.coordinator { + coordinator.animationHandler?(coordinator) + } + }, completion: { [weak self] (finished) in + self?.focusView.coordinator?.completionHandler?(finished) self?.focusView.coordinator = nil - }, completion:completeHandler) + completeHandler(finished) + }) } // MARK:- Internal diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index c240edf..988d012 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -113,11 +113,11 @@ extension OverlayViewController: PagingMenuViewControllerDelegate { func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) { guard let nextCell = viewController.cellForItem(at: index) else { return } - focusView.coordinator = { + focusView.coordinator?.animateFocusView(alongside: { _ in viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in cell.setFocusViewFrame(frame: nextCell.bounds, from: nextCell, animated: true) } - } + }, completion: nil) } } From 2e7f81a391df565bc4d5ed6e107da210a41b211e Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 00:54:59 +0900 Subject: [PATCH 09/13] change interface --- iOS Sample/iOS Sample/OverlayMenuCell.swift | 4 ++-- iOS Sample/iOS Sample/OverlayViewController.swift | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index 8a42850..aa0bfb0 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -91,9 +91,9 @@ class OverlayMenuCell: PagingMenuViewCell { maskLayer.path = UIBezierPath(roundedRect: maskLayer.bounds, cornerRadius: bounds.height / 2).cgPath } - func setFocusViewFrame(frame: CGRect, from view: UIView, animated: Bool) { + func setFrame(_ view: UIView, animated: Bool) { maskLayer.isHidden = false - let convertedFrame = view.layer.convert(frame, to: highlightTextLayer) + let convertedFrame = view.layer.convert(view.bounds, to: highlightTextLayer) CATransaction.setDisableActions(!animated) layoutMaskLayer(frame: convertedFrame) diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index 988d012..b411e11 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -43,9 +43,9 @@ class OverlayViewController: UIViewController { focusView = UINib(nibName: "OverlayFocusView", bundle: nil).instantiate(withOwner: self, options: nil).first as? OverlayFocusView menuViewController?.register(nib: UINib(nibName: "OverlayMenuCell", bundle: nil), forCellWithReuseIdentifier: "identifier") menuViewController?.registerFocusView(view: focusView, isBehindCell: true) - menuViewController?.reloadData(with: 0, completionHandler: { [weak self, focusView = focusView!] (vc) in + menuViewController?.reloadData(with: 0, completionHandler: { [weak self, menuViewController = menuViewController!] (vc) in let cell = self?.menuViewController.currentFocusedCell as! OverlayMenuCell - cell.setFocusViewFrame(frame: focusView.frame, from: focusView, animated: false) + cell.setFrame(menuViewController.focusView, animated: false) }) contentViewController?.reloadData(with: 0) } @@ -74,9 +74,7 @@ extension OverlayViewController: PagingMenuViewControllerDataSource { let cell = viewController.dequeueReusableCell(withReuseIdentifier: "identifier", for: index) as! OverlayMenuCell cell.highlightTextLayer.string = dataSource[index].menu cell.baseTextLayer.string = dataSource[index].menu - if let focusedCell = viewController.currentFocusedCell { - cell.setFocusViewFrame(frame: focusedCell.frame, from: focusedCell, animated: false) - } + cell.setFrame(viewController.focusView, animated: false) return cell } @@ -115,7 +113,7 @@ extension OverlayViewController: PagingMenuViewControllerDelegate { guard let nextCell = viewController.cellForItem(at: index) else { return } focusView.coordinator?.animateFocusView(alongside: { _ in viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - cell.setFocusViewFrame(frame: nextCell.bounds, from: nextCell, animated: true) + cell.setFrame(nextCell, animated: true) } }, completion: nil) } @@ -126,7 +124,7 @@ extension OverlayViewController: PagingContentViewControllerDelegate { menuViewController?.scroll(index: index, percent: percent, animated: false) menuViewController.visibleCells.forEach { let cell = $0 as! OverlayMenuCell - cell.setFocusViewFrame(frame: focusView.bounds, from: focusView, animated: false) + cell.setFrame(focusView, animated: false) } } } From 2a229656c94c9bd8febd65c141e13269e3bdb612 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 11:00:53 +0900 Subject: [PATCH 10/13] change layer to view --- PagingKit/PagingMenuView.swift | 10 ++- iOS Sample/iOS Sample/OverlayMenuCell.swift | 89 +++++++++---------- .../iOS Sample/OverlayViewController.swift | 28 +++--- 3 files changed, 66 insertions(+), 61 deletions(-) diff --git a/PagingKit/PagingMenuView.swift b/PagingKit/PagingMenuView.swift index a024430..b29a033 100644 --- a/PagingKit/PagingMenuView.swift +++ b/PagingKit/PagingMenuView.swift @@ -55,9 +55,17 @@ open class PagingMenuViewCell: UIView { } open class PagingMenuFocusViewAnimationCoordinator { + public let beginFrame: CGRect + public let endFrame: CGRect + fileprivate var animationHandler: ((PagingMenuFocusViewAnimationCoordinator) -> Void)? fileprivate var completionHandler: ((Bool) -> Void)? + init(beginFrame: CGRect, endFrame: CGRect) { + self.beginFrame = beginFrame + self.endFrame = endFrame + } + open func animateFocusView(alongside animation: @escaping (PagingMenuFocusViewAnimationCoordinator) -> Void, completion: ((Bool) -> Void)?) { animationHandler = animation completionHandler = completion @@ -390,7 +398,7 @@ open class PagingMenuView: UIScrollView { focusView.selectedIndex = index visibleCells.selectCell(with: index) - focusView.coordinator = PagingMenuFocusViewAnimationCoordinator() + focusView.coordinator = PagingMenuFocusViewAnimationCoordinator(beginFrame: focusView.frame, endFrame: itemFrame) menuDelegate?.pagingMenuView(pagingMenuView: self, willAnimate: focusView, at: index) UIView.perform(.delete, on: [], options: UIView.AnimationOptions(rawValue: 0), animations: { [weak self] in self?.contentOffset = offset diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index aa0bfb0..297d639 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -27,32 +27,29 @@ import PagingKit class OverlayMenuCell: PagingMenuViewCell { static let sizingCell = UINib(nibName: "OverlayMenuCell", bundle: nil).instantiate(withOwner: self, options: nil).first as! OverlayMenuCell - let maskLayer: CAShapeLayer = { - let layer = CAShapeLayer() - layer.fillColor = UIColor.black.cgColor - return layer + + let textMaskView: UIView = { + let view = UIView() + view.backgroundColor = .black + return view }() - let highlightTextLayer: CATextLayer = { - let layer = CATextLayer() - let font = UIFont.systemFont(ofSize: 16) - layer.font = font - layer.fontSize = font.pointSize - layer.foregroundColor = UIColor.white.cgColor - layer.contentsScale = UIScreen.main.scale - layer.alignmentMode = .center - return layer + let highlightLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = UIFont.systemFont(ofSize: 16) + label.textColor = UIColor.white + label.textAlignment = .center + return label }() - let baseTextLayer: CATextLayer = { - let layer = CATextLayer() - let font = UIFont.systemFont(ofSize: 16) - layer.font = font - layer.fontSize = font.pointSize - layer.foregroundColor = UIColor.black.cgColor - layer.contentsScale = UIScreen.main.scale - layer.alignmentMode = .center - return layer + let titleLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = UIFont.systemFont(ofSize: 16) + label.textColor = UIColor.black + label.textAlignment = .center + return label }() @IBOutlet weak var textLabel: UILabel! @@ -60,40 +57,38 @@ class OverlayMenuCell: PagingMenuViewCell { override func awakeFromNib() { super.awakeFromNib() textLabel.isHidden = true - maskLayer.isHidden = true - layer.addSublayer(baseTextLayer) - highlightTextLayer.mask = maskLayer - layer.addSublayer(highlightTextLayer) + addSubview(titleLabel) + highlightLabel.mask = textMaskView + addSubview(highlightLabel) + + do { + titleLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true + titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + titleLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + titleLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + } + + do { + highlightLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true + highlightLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + highlightLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + highlightLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + } } override func layoutSubviews() { super.layoutSubviews() - if let string = highlightTextLayer.string as? NSString, let font = highlightTextLayer.font as? UIFont { - let stringBounds = string.boundingRect(with: CGSize(width: .max, height: .min), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font], context: nil) - highlightTextLayer.frame = stringBounds - highlightTextLayer.frame.origin.y = (bounds.height - stringBounds.height) / 2 - highlightTextLayer.frame.origin.x = (bounds.width - stringBounds.width) / 2 - } - if let string = baseTextLayer.string as? NSString, let font = baseTextLayer.font as? UIFont { - let stringBounds = string.boundingRect(with: CGSize(width: .max, height: .min), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font], context: nil) - baseTextLayer.frame = stringBounds - baseTextLayer.frame.origin.y = (bounds.height - stringBounds.height) / 2 - baseTextLayer.frame.origin.x = (bounds.width - stringBounds.width) / 2 - } - - maskLayer.bounds = bounds.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) - maskLayer.path = UIBezierPath(roundedRect: maskLayer.bounds, cornerRadius: bounds.height / 2).cgPath - + textMaskView.bounds = bounds.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) } private func layoutMaskLayer(frame: CGRect) { - maskLayer.frame = frame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) - maskLayer.path = UIBezierPath(roundedRect: maskLayer.bounds, cornerRadius: bounds.height / 2).cgPath + textMaskView.frame = frame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) } - func setFrame(_ view: UIView, animated: Bool) { - maskLayer.isHidden = false - let convertedFrame = view.layer.convert(view.bounds, to: highlightTextLayer) + + func setFrame(_ view: UIView, frame: CGRect, animated: Bool) { + textMaskView.isHidden = false + let convertedFrame = view.convert(frame, to: highlightLabel) CATransaction.setDisableActions(!animated) layoutMaskLayer(frame: convertedFrame) diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index b411e11..cdbee40 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -36,16 +36,13 @@ class OverlayViewController: UIViewController { var menuViewController: PagingMenuViewController! var contentViewController: PagingContentViewController! - var focusView: OverlayFocusView! - override func viewDidLoad() { super.viewDidLoad() - focusView = UINib(nibName: "OverlayFocusView", bundle: nil).instantiate(withOwner: self, options: nil).first as? OverlayFocusView menuViewController?.register(nib: UINib(nibName: "OverlayMenuCell", bundle: nil), forCellWithReuseIdentifier: "identifier") - menuViewController?.registerFocusView(view: focusView, isBehindCell: true) + menuViewController?.registerFocusView(nib: UINib(nibName: "OverlayFocusView", bundle: nil), isBehindCell: true) menuViewController?.reloadData(with: 0, completionHandler: { [weak self, menuViewController = menuViewController!] (vc) in let cell = self?.menuViewController.currentFocusedCell as! OverlayMenuCell - cell.setFrame(menuViewController.focusView, animated: false) + cell.setFrame(menuViewController.menuView, frame: menuViewController.focusView.frame, animated: false) }) contentViewController?.reloadData(with: 0) } @@ -72,9 +69,9 @@ class OverlayViewController: UIViewController { extension OverlayViewController: PagingMenuViewControllerDataSource { func menuViewController(viewController: PagingMenuViewController, cellForItemAt index: Int) -> PagingMenuViewCell { let cell = viewController.dequeueReusableCell(withReuseIdentifier: "identifier", for: index) as! OverlayMenuCell - cell.highlightTextLayer.string = dataSource[index].menu - cell.baseTextLayer.string = dataSource[index].menu - cell.setFrame(viewController.focusView, animated: false) + cell.titleLabel.text = dataSource[index].menu + cell.highlightLabel.text = dataSource[index].menu + cell.setFrame(viewController.menuView, frame: cell.frame, animated: false) return cell } @@ -110,10 +107,15 @@ extension OverlayViewController: PagingMenuViewControllerDelegate { } func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) { - guard let nextCell = viewController.cellForItem(at: index) else { return } - focusView.coordinator?.animateFocusView(alongside: { _ in + guard let coordinator = focusView.coordinator else { return } + + viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in + cell.setFrame(viewController.menuView, frame: coordinator.beginFrame, animated: true) + } + + coordinator.animateFocusView(alongside: { coordinator in viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - cell.setFrame(nextCell, animated: true) + cell.setFrame(viewController.menuView, frame: coordinator.endFrame, animated: true) } }, completion: nil) } @@ -121,10 +123,10 @@ extension OverlayViewController: PagingMenuViewControllerDelegate { extension OverlayViewController: PagingContentViewControllerDelegate { func contentViewController(viewController: PagingContentViewController, didManualScrollOn index: Int, percent: CGFloat) { - menuViewController?.scroll(index: index, percent: percent, animated: false) + menuViewController.scroll(index: index, percent: percent, animated: false) menuViewController.visibleCells.forEach { let cell = $0 as! OverlayMenuCell - cell.setFrame(focusView, animated: false) + cell.setFrame(menuViewController.menuView, frame: menuViewController.focusView.frame, animated: false) } } } From 5d19a3b6026fd6011e4cee92bf18edbbf65301b4 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 11:10:22 +0900 Subject: [PATCH 11/13] simplified sample code --- iOS Sample/iOS Sample/OverlayMenuCell.swift | 56 +++---------------- iOS Sample/iOS Sample/OverlayMenuCell.xib | 17 +++++- .../iOS Sample/OverlayViewController.swift | 12 ++-- 3 files changed, 27 insertions(+), 58 deletions(-) diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.swift b/iOS Sample/iOS Sample/OverlayMenuCell.swift index 297d639..3e4e1f5 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.swift +++ b/iOS Sample/iOS Sample/OverlayMenuCell.swift @@ -28,70 +28,28 @@ import PagingKit class OverlayMenuCell: PagingMenuViewCell { static let sizingCell = UINib(nibName: "OverlayMenuCell", bundle: nil).instantiate(withOwner: self, options: nil).first as! OverlayMenuCell + let maskInsets = UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8) + let textMaskView: UIView = { let view = UIView() view.backgroundColor = .black return view }() - let highlightLabel: UILabel = { - let label = UILabel() - label.translatesAutoresizingMaskIntoConstraints = false - label.font = UIFont.systemFont(ofSize: 16) - label.textColor = UIColor.white - label.textAlignment = .center - return label - }() - - let titleLabel: UILabel = { - let label = UILabel() - label.translatesAutoresizingMaskIntoConstraints = false - label.font = UIFont.systemFont(ofSize: 16) - label.textColor = UIColor.black - label.textAlignment = .center - return label - }() - - @IBOutlet weak var textLabel: UILabel! + @IBOutlet var highlightLabel: UILabel! + @IBOutlet var titleLabel: UILabel! override func awakeFromNib() { super.awakeFromNib() - textLabel.isHidden = true - addSubview(titleLabel) highlightLabel.mask = textMaskView - addSubview(highlightLabel) - - do { - titleLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true - titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - titleLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true - titleLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true - } - - do { - highlightLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true - highlightLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - highlightLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true - highlightLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true - } } override func layoutSubviews() { super.layoutSubviews() - textMaskView.bounds = bounds.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) - } - - private func layoutMaskLayer(frame: CGRect) { - textMaskView.frame = frame.inset(by: UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8)) + textMaskView.bounds = bounds.inset(by: maskInsets) } - - func setFrame(_ view: UIView, frame: CGRect, animated: Bool) { - textMaskView.isHidden = false - let convertedFrame = view.convert(frame, to: highlightLabel) - - CATransaction.setDisableActions(!animated) - layoutMaskLayer(frame: convertedFrame) - CATransaction.setDisableActions(false) + func setFrame(_ menuView: PagingMenuView, maskFrame: CGRect, animated: Bool) { + textMaskView.frame = menuView.convert(maskFrame, to: highlightLabel).inset(by: maskInsets) } } diff --git a/iOS Sample/iOS Sample/OverlayMenuCell.xib b/iOS Sample/iOS Sample/OverlayMenuCell.xib index cb2109a..436dd97 100644 --- a/iOS Sample/iOS Sample/OverlayMenuCell.xib +++ b/iOS Sample/iOS Sample/OverlayMenuCell.xib @@ -1,11 +1,11 @@ - + - + @@ -21,9 +21,19 @@ + + + + + @@ -31,7 +41,8 @@ - + + diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index cdbee40..46d1254 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -42,7 +42,7 @@ class OverlayViewController: UIViewController { menuViewController?.registerFocusView(nib: UINib(nibName: "OverlayFocusView", bundle: nil), isBehindCell: true) menuViewController?.reloadData(with: 0, completionHandler: { [weak self, menuViewController = menuViewController!] (vc) in let cell = self?.menuViewController.currentFocusedCell as! OverlayMenuCell - cell.setFrame(menuViewController.menuView, frame: menuViewController.focusView.frame, animated: false) + cell.setFrame(menuViewController.menuView, maskFrame: menuViewController.focusView.frame, animated: false) }) contentViewController?.reloadData(with: 0) } @@ -71,13 +71,13 @@ extension OverlayViewController: PagingMenuViewControllerDataSource { let cell = viewController.dequeueReusableCell(withReuseIdentifier: "identifier", for: index) as! OverlayMenuCell cell.titleLabel.text = dataSource[index].menu cell.highlightLabel.text = dataSource[index].menu - cell.setFrame(viewController.menuView, frame: cell.frame, animated: false) + cell.setFrame(viewController.menuView, maskFrame: cell.frame, animated: false) return cell } func menuViewController(viewController: PagingMenuViewController, widthForItemAt index: Int) -> CGFloat { - OverlayMenuCell.sizingCell.textLabel.text = dataSource[index].menu + OverlayMenuCell.sizingCell.titleLabel.text = dataSource[index].menu var referenceSize = UIView.layoutFittingCompressedSize referenceSize.height = viewController.view.bounds.height let size = OverlayMenuCell.sizingCell.systemLayoutSizeFitting(referenceSize, withHorizontalFittingPriority: UILayoutPriority.defaultLow, verticalFittingPriority: UILayoutPriority.defaultHigh) @@ -110,12 +110,12 @@ extension OverlayViewController: PagingMenuViewControllerDelegate { guard let coordinator = focusView.coordinator else { return } viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - cell.setFrame(viewController.menuView, frame: coordinator.beginFrame, animated: true) + cell.setFrame(viewController.menuView, maskFrame: coordinator.beginFrame, animated: true) } coordinator.animateFocusView(alongside: { coordinator in viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in - cell.setFrame(viewController.menuView, frame: coordinator.endFrame, animated: true) + cell.setFrame(viewController.menuView, maskFrame: coordinator.endFrame, animated: true) } }, completion: nil) } @@ -126,7 +126,7 @@ extension OverlayViewController: PagingContentViewControllerDelegate { menuViewController.scroll(index: index, percent: percent, animated: false) menuViewController.visibleCells.forEach { let cell = $0 as! OverlayMenuCell - cell.setFrame(menuViewController.menuView, frame: menuViewController.focusView.frame, animated: false) + cell.setFrame(menuViewController.menuView, maskFrame: menuViewController.focusView.frame, animated: false) } } } From 88c67bbf878edf6bf2a8148860ab08a717340762 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 11:24:15 +0900 Subject: [PATCH 12/13] add documantation --- PagingKit/PagingMenuView.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/PagingKit/PagingMenuView.swift b/PagingKit/PagingMenuView.swift index b29a033..00508dd 100644 --- a/PagingKit/PagingMenuView.swift +++ b/PagingKit/PagingMenuView.swift @@ -54,8 +54,13 @@ open class PagingMenuViewCell: UIView { public internal(set) var index: Int! } + +/// A set of methods that provides support for animations associated with a focus view transition. +/// You can use a coordinator object to perform tasks that are related to a transition but that are separate from what the animator objects are doing. open class PagingMenuFocusViewAnimationCoordinator { + /// A frame at the start position public let beginFrame: CGRect + /// A frame at the end position public let endFrame: CGRect fileprivate var animationHandler: ((PagingMenuFocusViewAnimationCoordinator) -> Void)? @@ -66,6 +71,11 @@ open class PagingMenuFocusViewAnimationCoordinator { self.endFrame = endFrame } + /// Runs the specified animations at the same time as the focus view animations. + /// + /// - Parameters: + /// - animation: A block containing the animations you want to perform. These animations run in the same context as the focus view animations and therefore have the same default attributes. + /// - completion: The block of code to execute after the animation finishes. You may specify nil for this open func animateFocusView(alongside animation: @escaping (PagingMenuFocusViewAnimationCoordinator) -> Void, completion: ((Bool) -> Void)?) { animationHandler = animation completionHandler = completion @@ -84,7 +94,9 @@ open class PagingMenuFocusView: UIView { required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + + /// A focus view makes this object when starting animation and destroy when finishing animation. + /// You can make a custom animation along a focus view animation. open var coordinator: PagingMenuFocusViewAnimationCoordinator? } From 479bd78d755e941d3e0178a80a98eef924daae16 Mon Sep 17 00:00:00 2001 From: Kazuhiro Hayashi Date: Sat, 1 Dec 2018 11:40:21 +0900 Subject: [PATCH 13/13] fix interface --- PagingKit/PagingMenuView.swift | 35 ++++++++++--------- PagingKit/PagingMenuViewController.swift | 15 +++++--- .../iOS Sample/OverlayViewController.swift | 5 +-- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/PagingKit/PagingMenuView.swift b/PagingKit/PagingMenuView.swift index 00508dd..fb0deb7 100644 --- a/PagingKit/PagingMenuView.swift +++ b/PagingKit/PagingMenuView.swift @@ -94,10 +94,6 @@ open class PagingMenuFocusView: UIView { required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - - /// A focus view makes this object when starting animation and destroy when finishing animation. - /// You can make a custom animation along a focus view animation. - open var coordinator: PagingMenuFocusViewAnimationCoordinator? } /** @@ -142,12 +138,19 @@ public protocol PagingMenuViewDelegate: class { /// - pagingMenuView: The paging menu view requesting this information. /// - index: The index that specifies the location of the item. func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) - func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView, at index: Int) + + /// Notifies the menu view that the frame of its focus view is about to change. + /// + /// - Parameters: + /// - pagingMenuView: a menu view object informing the delegate. + /// - index: end index + /// - coordinator: animator coordinator + func pagingMenuView(pagingMenuView: PagingMenuView, willAnimateFocusViewTo index: Int, with coordinator: PagingMenuFocusViewAnimationCoordinator) } public extension PagingMenuViewDelegate { public func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) {} - public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView, at index: Int) {} + public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimateFocusViewTo index: Int, with coordinator: PagingMenuFocusViewAnimationCoordinator) {} } /// Displays menu lists of information and supports selection and paging of the information. @@ -410,18 +413,16 @@ open class PagingMenuView: UIScrollView { focusView.selectedIndex = index visibleCells.selectCell(with: index) - focusView.coordinator = PagingMenuFocusViewAnimationCoordinator(beginFrame: focusView.frame, endFrame: itemFrame) - menuDelegate?.pagingMenuView(pagingMenuView: self, willAnimate: focusView, at: index) + let coordinator = PagingMenuFocusViewAnimationCoordinator(beginFrame: focusView.frame, endFrame: itemFrame) + menuDelegate?.pagingMenuView(pagingMenuView: self, willAnimateFocusViewTo: index, with: coordinator) UIView.perform(.delete, on: [], options: UIView.AnimationOptions(rawValue: 0), animations: { [weak self] in - self?.contentOffset = offset - self?.focusView.frame = itemFrame - self?.focusView.layoutIfNeeded() - if let coordinator = self?.focusView.coordinator { - coordinator.animationHandler?(coordinator) - } - }, completion: { [weak self] (finished) in - self?.focusView.coordinator?.completionHandler?(finished) - self?.focusView.coordinator = nil + guard let _self = self else { return } + _self.contentOffset = offset + _self.focusView.frame = itemFrame + _self.focusView.layoutIfNeeded() + coordinator.animationHandler?(coordinator) + }, completion: { (finished) in + coordinator.completionHandler?(finished) completeHandler(finished) }) } diff --git a/PagingKit/PagingMenuViewController.swift b/PagingKit/PagingMenuViewController.swift index 8e7ef7f..90e7be1 100644 --- a/PagingKit/PagingMenuViewController.swift +++ b/PagingKit/PagingMenuViewController.swift @@ -42,13 +42,20 @@ public protocol PagingMenuViewControllerDelegate: class { /// - previousPage: An page number previously focusing menu in menu view controller. func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) - func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) + + /// Notifies the menu view controller that the frame of its focus view is about to change. + /// + /// - Parameters: + /// - viewController: A menu view controller object informing the delegate. + /// - index: end index + /// - coordinator: animator coordinator + func menuViewController(viewController: PagingMenuViewController, willAnimateFocusViewTo index: Int, with coordinator: PagingMenuFocusViewAnimationCoordinator) } extension PagingMenuViewControllerDelegate { public func menuViewController(viewController: PagingMenuViewController, focusViewDidEndTransition focusView: PagingMenuFocusView) {} - public func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) {} + public func menuViewController(viewController: PagingMenuViewController, willAnimateFocusViewTo index: Int, with coordinator: PagingMenuFocusViewAnimationCoordinator) {} } /// The data source provides the paging menu view controller object with the information it needs to construct and modify the menus. @@ -302,8 +309,8 @@ extension PagingMenuViewController: UIScrollViewDelegate { // MARK:- PagingMenuViewDelegate extension PagingMenuViewController: PagingMenuViewDelegate { - public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimate focusView: PagingMenuFocusView, at index: Int) { - delegate?.menuViewController(viewController: self, willAnimate: focusView, at: index) + public func pagingMenuView(pagingMenuView: PagingMenuView, willAnimateFocusViewTo index: Int, with coordinator: PagingMenuFocusViewAnimationCoordinator) { + delegate?.menuViewController(viewController: self, willAnimateFocusViewTo: index, with: coordinator) } public func pagingMenuView(pagingMenuView: PagingMenuView, didSelectItemAt index: Int) { diff --git a/iOS Sample/iOS Sample/OverlayViewController.swift b/iOS Sample/iOS Sample/OverlayViewController.swift index 46d1254..bcbe395 100644 --- a/iOS Sample/iOS Sample/OverlayViewController.swift +++ b/iOS Sample/iOS Sample/OverlayViewController.swift @@ -103,12 +103,9 @@ extension OverlayViewController: PagingContentViewControllerDataSource { extension OverlayViewController: PagingMenuViewControllerDelegate { func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) { contentViewController?.scroll(to: page, animated: true) - } - func menuViewController(viewController: PagingMenuViewController, willAnimate focusView: PagingMenuFocusView, at index: Int) { - guard let coordinator = focusView.coordinator else { return } - + func menuViewController(viewController: PagingMenuViewController, willAnimateFocusViewTo index: Int, with coordinator: PagingMenuFocusViewAnimationCoordinator) { viewController.visibleCells.compactMap { $0 as? OverlayMenuCell }.forEach { cell in cell.setFrame(viewController.menuView, maskFrame: coordinator.beginFrame, animated: true) }