diff --git a/MVMCoreUI/Molecules/Carousel.swift b/MVMCoreUI/Molecules/Carousel.swift index cb0bbf2b..cb5d7243 100644 --- a/MVMCoreUI/Molecules/Carousel.swift +++ b/MVMCoreUI/Molecules/Carousel.swift @@ -45,7 +45,6 @@ open class Carousel: ViewConstrainingView { /// If the carousel should loop after scrolling past the first and final cells. var loop = false private var dragging = false - private var previousContentOffsetX: CGFloat = 0 // MARK: - MVMCoreViewProtocol open override func setupView() { @@ -73,6 +72,7 @@ open class Carousel: ViewConstrainingView { DispatchQueue.main.async { self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false) self.collectionView.layoutIfNeeded() + self.showPeaking(true) } } @@ -201,6 +201,24 @@ open class Carousel: ViewConstrainingView { } self.pagingView = pagingView } + + open func showPeaking(_ peaking: Bool) { + if peaking { + // Show overlay and arrow in peaking Cell + let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row } + if let firstItem = visibleItemsPaths.first, firstItem.row != currentIndex { + (collectionView.cellForItem(at: firstItem) as? MoleculeCollectionViewCell)?.setPeaking(true, animated: true) + } + if let lastItem = visibleItemsPaths.last, lastItem.row != currentIndex { + (collectionView.cellForItem(at: lastItem) as? MoleculeCollectionViewCell)?.setPeaking(true, animated: true) + } + } else { + // Hide peaking. + for item in collectionView.visibleCells { + (item as? MoleculeCollectionViewCell)?.setPeaking(false, animated: true) + } + } + } } extension Carousel: UICollectionViewDelegateFlowLayout { @@ -232,68 +250,6 @@ extension Carousel: UICollectionViewDataSource { } extension Carousel: UIScrollViewDelegate { - /*// For getting the scroll progress to set the page control color progress. - - (CGFloat)getPageControlPercentBasedOnScrollView:(UIScrollView *)scrollView { - CGFloat cardWidth = ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).itemSize.width; - CGFloat separatorWidth = ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).minimumLineSpacing; - CGFloat contentOfsetInCard = fmodf(scrollView.contentOffset.x, cardWidth + separatorWidth); - CGFloat endThresholdPageControl = cardWidth + separatorWidth - CGRectGetMaxX(self.pageControl.frame); - CGFloat progress = contentOfsetInCard - endThresholdPageControl; - CGFloat width = CGRectGetWidth(self.pageControl.bounds); - CGFloat percent = (width - progress)/width; - CGFloat cappedPercent = MAX(MIN(percent, 1), 0); - return cappedPercent; - } - - - (void)setPageControlColorsBasedOnScrollView:(UIScrollView *)scrollView { - - // Check if we will need to change colors. - BOOL needToShiftColors = NO; - NSInteger nextCardIndex = 0; - CGFloat cardWidth = ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).itemSize.width; - CGFloat separatorWidth = ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).minimumLineSpacing; - NSInteger currentCard = scrollView.contentOffset.x / (cardWidth + separatorWidth); - CGFloat cardStart = currentCard * (cardWidth + separatorWidth); - CGFloat cardEnd = cardStart + cardWidth + separatorWidth; - NSInteger pageIndicator = currentCard; - if ((self.previousContentOffsetX == NSNotFound || self.previousContentOffsetX <= cardStart) && scrollView.contentOffset.x >= cardStart) { - - // We are passed the threshold and moving right, change to right card color. - needToShiftColors = YES; - nextCardIndex = currentCard + 1; - pageIndicator = currentCard - 1; - } else if ((self.previousContentOffsetX == NSNotFound || self.previousContentOffsetX >= cardEnd) && scrollView.contentOffset.x < cardEnd) { - - // We are passed the threshold and moving left, change to left card color. - needToShiftColors = YES; - nextCardIndex = currentCard - 1; - } - - if (needToShiftColors) { - // Only shift the page control if we are dragging still, otherwise end animation will control. - if (self.dragging) { - [self.pageControl setCurrentPage:pageIndicator]; - } - - // Get the current page color - NSString *colorString = [[self.feedModules objectAtIndex:currentCard] string:KeyPageIndicatorColor]; - UIColor *currentCardPageControlColor = colorString ? [UIColor mfGetColorForHex:colorString] : [UIColor blackColor]; - - // Get the next page color and set accordingly. - colorString = [[self.feedModules dictionaryAtIndex:nextCardIndex] string:KeyPageIndicatorColor]; - UIColor *nextCardPageControlColor = colorString ? [UIColor mfGetColorForHex:colorString] : [UIColor blackColor]; - - // Which color needs to be on top or bottom depends on which direction we are moving. - if (nextCardIndex > currentCard) { - [self setPageControlColor:nextCardPageControlColor progressColor:currentCardPageControlColor]; - } else { - [self setPageControlColor:currentCardPageControlColor progressColor:nextCardPageControlColor]; - } - } - } - - */ - func handleUserOnBufferCell() { guard loop else { return @@ -302,7 +258,6 @@ extension Carousel: UIScrollViewDelegate { let lastPageIndex = numberOfPages + 1 let goToIndex = {(index: Int) in self.currentIndex = index - self.previousContentOffsetX = CGFloat(NSNotFound) self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false) self.collectionView.layoutIfNeeded() self.pagingView?.setPage(self.pageIndex) @@ -337,24 +292,17 @@ extension Carousel: UIScrollViewDelegate { } public func scrollViewDidScroll(_ scrollView: UIScrollView) { + // Check if the user is dragging the card even further past the next card. checkForDraggingOutOfBounds(scrollView) - // Set the page control direction colors if needed. - //[self setPageControlColorsBasedOnScrollView:scrollView]; - - // Set the percent of progress. - //self.pageControl.progressView.progress = [self getPageControlPercentBasedOnScrollView:scrollView]; - - previousContentOffsetX = scrollView.contentOffset.x; + // Let the pager know our progress if needed. + pagingView?.scrollViewDidScroll?(collectionView) } public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { dragging = true - -// // Hide coverview and arrow. -// FeedBaseCollectionViewCell *peakingCell = (FeedBaseCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.currentIndex + 1 inSection:0]]; -// [peakingCell setPeaking:NO animated:YES]; + showPeaking(false) } public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { @@ -363,7 +311,7 @@ extension Carousel: UIScrollViewDelegate { // This is for setting up smooth custom paging. (Since UICollectionView only handles paging based on collection view size and not cell size). guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { - return + return } // We switch cards if we pass the velocity threshold or position threshold (currently 50%). @@ -381,11 +329,6 @@ extension Carousel: UIScrollViewDelegate { currentIndex = min(max(cellToSwipeTo, 0), lastCellIndex) collectionView.scrollToItem(at: IndexPath(row: currentIndex, section: 0), at: itemAlignment, animated: true) - - // Notify that card changed - /*if (self.cardChanged) { - self.cardChanged(self.currentIndex, [self.module string:[MFBaseHomeViewController getFeedContainerNameKey]]); - }*/ } // To give the illusion of endless scrolling. Since we are always calling scrollToItem we can assume finished paging in here. @@ -394,27 +337,7 @@ extension Carousel: UIScrollViewDelegate { handleUserOnBufferCell() pagingView?.setPage(pageIndex) - /* - // Update to the new page in the control if needed. - if (self.currentIndex - 1 != self.pageControl.currentPage) { - [self.pageControl setCurrentPage:self.currentIndex - 1]; - } - // Show overlay and arrow in next Cell - FeedBaseCollectionViewCell *nextCell = (FeedBaseCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.currentIndex + 1 inSection:0]]; - [nextCell setPeaking:YES animated:YES]; - FeedBaseCollectionViewCell *currentCell = (FeedBaseCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.currentIndex inSection:0]]; - if (currentCell) { - self.accessibilityElements = @[currentCell.containerView, self.pageControl]; - currentCell.containerView.isAccessibilityElement = YES; - currentCell.accessibilityElementsHidden = NO; - UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification,currentCell.containerView); - } - - // Set the page control again if pageControl is tapped or voice over is using. - [self setPageControlColorsBasedOnScrollView:scrollView]; - - // send adobe tracker action - [self sendAdobeTrackerAction];*/ + showPeaking(true) } } diff --git a/MVMCoreUI/Molecules/MVMCoreUIPagingProtocol.h b/MVMCoreUI/Molecules/MVMCoreUIPagingProtocol.h index ac8aee3c..89dd279b 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIPagingProtocol.h +++ b/MVMCoreUI/Molecules/MVMCoreUIPagingProtocol.h @@ -18,5 +18,8 @@ typedef void (^PagingTouchBlock)(NSObject* _Nonnull sen - (void)setPage:(NSInteger)page; - (void)setPagingTouchBlock:(nullable PagingTouchBlock)pagingTouchBlock; + +@optional +- (void)scrollViewDidScroll:(nonnull UICollectionView *)collectionView; @end diff --git a/MVMCoreUI/Molecules/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/MoleculeCollectionViewCell.swift index 2eb61e68..23850e70 100644 --- a/MVMCoreUI/Molecules/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Molecules/MoleculeCollectionViewCell.swift @@ -11,6 +11,11 @@ import UIKit open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol { open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + open var allowsPeaking = false + var peakingLeftArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) + var peakingRightArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) + var peakingCover = MVMCoreUICommonViewsUtility.commonView() + public override init(frame: CGRect) { super.init(frame: .zero) setupView() @@ -22,15 +27,49 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi } public func setupView() { + guard peakingCover.superview == nil else { + return + } + // Covers the card when peaking. + peakingCover.backgroundColor = .white + peakingCover.alpha = 0 + contentView.addSubview(peakingCover) + NSLayoutConstraint.constraintPinSubview(toSuperview: peakingCover) + + // A small arrow on the next card for when peaking. + let ratio: CGFloat = 0.015 + peakingLeftArrow.translatesAutoresizingMaskIntoConstraints = false + peakingLeftArrow.alpha = 0 + peakingLeftArrow.tintColor = .black + contentView.addSubview(peakingLeftArrow) + peakingLeftArrow.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true + NSLayoutConstraint.scalingPinViewLeft(toSuper: peakingLeftArrow, ratio: ratio, anchor: contentView.widthAnchor) + + peakingRightArrow.translatesAutoresizingMaskIntoConstraints = false + peakingRightArrow.transform = CGAffineTransform(scaleX: -1, y: 1) // Flip + peakingRightArrow.alpha = 0 + peakingRightArrow.tintColor = .black + contentView.addSubview(peakingRightArrow) + peakingRightArrow.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true + NSLayoutConstraint.scalingPinViewRight(toSuper: peakingRightArrow, ratio: ratio, anchor: contentView.widthAnchor) } public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { + + // Handles peaking. + allowsPeaking = json?.optionalBoolForKey("peakingUI") ?? true + if let peakingArrowColor = json?.optionalStringForKey("peakingArrowColor") { + let color = UIColor.mfGet(forHex: peakingArrowColor) + peakingLeftArrow.tintColor = color + peakingRightArrow.tintColor = color + } + + guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule) else { return } if molecule == nil { if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { - contentView.addSubview(moleculeView) + contentView.insertSubview(moleculeView, at: 0) let standardConstraints = (moleculeView as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values)) if standardConstraints { @@ -61,4 +100,21 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi public func updateView(_ size: CGFloat) { molecule?.updateView(size) } + + public func setPeaking(_ peaking: Bool, animated: Bool) { + guard allowsPeaking else { + return + } + let animation = {() in + self.peakingRightArrow.alpha = peaking ? 1 : 0 + self.peakingLeftArrow.alpha = peaking ? 1 : 0 + self.peakingCover.alpha = peaking ? 0.5 : 0 + print("\(self.peakingCover.alpha)") + } + if animated { + UIView.animate(withDuration: 0.4, animations: animation) + } else { + animation() + } + } } diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/Contents.json new file mode 100644 index 00000000..0851ce22 --- /dev/null +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "E_UBI_003_G.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "E_UBI_003_G@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "E_UBI_003_G@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G.png b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G.png new file mode 100644 index 00000000..8c789309 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G@2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G@2x.png new file mode 100644 index 00000000..b6453e48 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G@2x.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G@3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G@3x.png new file mode 100644 index 00000000..7fe147c7 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/peakingRightArrow.imageset/E_UBI_003_G@3x.png differ